diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..d710461c --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-grpc": { + "version": "2.36.0", + "commands": [ + "dotnet-grpc" + ] + } + } +} \ No newline at end of file diff --git a/.github/workflows/dotnet-pack-master.yml b/.github/workflows/dotnet-pack-master.yml index 66806fb6..b50d9335 100644 --- a/.github/workflows/dotnet-pack-master.yml +++ b/.github/workflows/dotnet-pack-master.yml @@ -14,6 +14,8 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.100 + - name: Setup dotnet tools + run: dotnet tool restore - name: Test run: ./build.sh --target test --no-logo pack: @@ -26,6 +28,8 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.100 + - name: Setup dotnet tools + run: dotnet tool restore - name: Pack run: ./build.sh --no-logo --version 0.0.0-dev.${GITHUB_SHA::8} --release-notes 'This is a package built from master branch.' --target Pack - uses: actions/upload-artifact@v2 diff --git a/.github/workflows/dotnet-release.yml b/.github/workflows/dotnet-release.yml index 16b37cc7..f4024def 100644 --- a/.github/workflows/dotnet-release.yml +++ b/.github/workflows/dotnet-release.yml @@ -1,6 +1,10 @@ name: .NET Release -on: [workflow_dispatch] +on: + push: + branches: + - master + - next jobs: test: @@ -11,6 +15,8 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.100 + - name: Setup dotnet tools + run: dotnet tool restore - name: Test run: ./build.sh --target test --no-logo semantic-release: @@ -24,6 +30,8 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.100 + - name: Setup dotnet tools + run: dotnet tool restore - name: Semantic Release uses: cycjimmy/semantic-release-action@v2 with: diff --git a/.github/workflows/dotnet-test.yml b/.github/workflows/dotnet-test.yml index c93011cf..c3e8e77c 100644 --- a/.github/workflows/dotnet-test.yml +++ b/.github/workflows/dotnet-test.yml @@ -18,5 +18,7 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.100 + - name: Setup dotnet tools + run: dotnet tool restore - name: Test run: ./build.sh --target test --no-logo diff --git a/.github/workflows/security-analysis.yml b/.github/workflows/security-analysis.yml index 1f43095a..ce6ab271 100644 --- a/.github/workflows/security-analysis.yml +++ b/.github/workflows/security-analysis.yml @@ -1,9 +1,6 @@ name: Code Security Testing on: - push: - branches: - - master pull_request: branches: - master @@ -39,6 +36,9 @@ jobs: with: dotnet-version: 5.0.100 + - name: Setup dotnet tools + run: dotnet tool restore + - name: Build run: ./build.sh --target compile --no-logo diff --git a/.releaserc.json b/.releaserc.json index e3a2c8ea..a7cb63b0 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -1,6 +1,12 @@ { - "verifyConditions": ["@semantic-release/github"], - "addChannel": ["@semantic-release/github"], + "branches": [ + "master", + { + "name": "next", + "prerelease": "prerelease" + } + ], + "plugins": ["@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/github"], "prepare": [ [ "@semantic-release/exec", @@ -13,7 +19,11 @@ [ "@semantic-release/github", { - "assets": [{ "path": "artifacts/*.nupkg" }] + "assets": [ + { + "path": "artifacts/*.nupkg" + } + ] } ], [ diff --git a/Zitadel.sln b/Zitadel.sln index 2edb7cd3..9b9148dd 100644 --- a/Zitadel.sln +++ b/Zitadel.sln @@ -32,6 +32,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zitadel.Spa.Dev", "tests\Zi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zitadel.Test", "tests\Zitadel.Test\Zitadel.Test.csproj", "{44543DC1-97C5-4525-8EE4-16E5389FDF4E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zitadel.Api", "src\Zitadel.Api\Zitadel.Api.csproj", "{0F83D36E-945F-4B70-A6E9-867C21C546EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zitadel.Api.Access.Dev", "tests\Zitadel.Api.Access.Dev\Zitadel.Api.Access.Dev.csproj", "{B51464E4-58A6-4C41-A0BF-35245F0DD862}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,6 +47,8 @@ Global {9AD0581A-82BA-4E96-A1EE-722D579C26D8} = {2462186B-61A8-476C-9674-176570BBEC35} {23DE9A2C-E9A0-458A-8878-9224C49FFB3B} = {2462186B-61A8-476C-9674-176570BBEC35} {44543DC1-97C5-4525-8EE4-16E5389FDF4E} = {2462186B-61A8-476C-9674-176570BBEC35} + {0F83D36E-945F-4B70-A6E9-867C21C546EB} = {47CEB49C-56A9-4BDF-BC66-54E407391D49} + {B51464E4-58A6-4C41-A0BF-35245F0DD862} = {2462186B-61A8-476C-9674-176570BBEC35} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BF13C638-6A5C-4BF6-89FF-1BAA3986F86A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -63,5 +69,13 @@ Global {44543DC1-97C5-4525-8EE4-16E5389FDF4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {44543DC1-97C5-4525-8EE4-16E5389FDF4E}.Release|Any CPU.ActiveCfg = Release|Any CPU {44543DC1-97C5-4525-8EE4-16E5389FDF4E}.Release|Any CPU.Build.0 = Release|Any CPU + {0F83D36E-945F-4B70-A6E9-867C21C546EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0F83D36E-945F-4B70-A6E9-867C21C546EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0F83D36E-945F-4B70-A6E9-867C21C546EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0F83D36E-945F-4B70-A6E9-867C21C546EB}.Release|Any CPU.Build.0 = Release|Any CPU + {B51464E4-58A6-4C41-A0BF-35245F0DD862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B51464E4-58A6-4C41-A0BF-35245F0DD862}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B51464E4-58A6-4C41-A0BF-35245F0DD862}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B51464E4-58A6-4C41-A0BF-35245F0DD862}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Zitadel.Api/ClientOptions.cs b/src/Zitadel.Api/ClientOptions.cs new file mode 100644 index 00000000..2b34ed6e --- /dev/null +++ b/src/Zitadel.Api/ClientOptions.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using Zitadel.Authentication; +using Zitadel.Authentication.Credentials; + +namespace Zitadel.Api +{ + /// + /// Options for an API client. + /// + public record ClientOptions + { + /// + /// The API endpoint for the client. This will be the base url for the api calls. + /// + public string Endpoint { get; init; } = ZitadelDefaults.ZitadelApiEndpoint; + + /// + /// The organizational context in the API. This essentially defines the "x-zitadel-orgid" header value + /// which provides the api with the orgId that the API call will be executed in. + /// This may be overwritten for specific calls. + /// + public string Organization { get; init; } = string.Empty; + + /// + /// Authentication token for the client. This field may not be used in conjunction with + /// . Use this field to explicitly set the + /// Bearer token that will be transmitted to the API. If no authentication method is set, + /// each call must attach the authorization header. + /// + public string? Token { get; init; } + + /// + /// Service Account authentication method. If this field is set, the API calls are + /// automatically authenticated with a and the corresponding + /// . This will renew the access token if it is + /// expired. + /// + public (ServiceAccount Account, ServiceAccount.AuthOptions AuthOptions)? ServiceAccountAuthentication + { + get; + init; + } + + /// + /// List of additional arbitrary headers that are attached to each call. + /// + public IDictionary? AdditionalHeaders { get; init; } + } +} diff --git a/src/Zitadel.Api/Clients.cs b/src/Zitadel.Api/Clients.cs new file mode 100644 index 00000000..4ebe389c --- /dev/null +++ b/src/Zitadel.Api/Clients.cs @@ -0,0 +1,75 @@ +using System; +using System.Net.Http; +using Caos.Zitadel.Admin.Api.V1; +using Caos.Zitadel.Auth.Api.V1; +using Caos.Zitadel.Management.Api.V1; +using Grpc.Core; +using Grpc.Net.Client; +using Zitadel.Authentication; + +namespace Zitadel.Api +{ + /// + /// Helper class to instantiate api service clients for the zitadel API with correct settings. + /// + public static class Clients + { + /// + /// Create a service client for the auth service. + /// + /// Options for the client like authorization method. + /// The . + public static AuthService.AuthServiceClient AuthService(ClientOptions options) => + GetClient(options); + + /// + /// Create a service client for the admin service. + /// + /// Options for the client like authorization method. + /// The . + public static AdminService.AdminServiceClient AdminService(ClientOptions options) => + GetClient(options); + + /// + /// Create a service client for the management service. + /// + /// Options for the client like authorization method. + /// The . + public static ManagementService.ManagementServiceClient ManagementService(ClientOptions options) => + GetClient(options); + + private static TClient GetClient(ClientOptions options) + where TClient : ClientBase + { + var httpClient = options.Token == null && options.ServiceAccountAuthentication != null + ? new HttpClient( + new ServiceAccountHttpHandler( + options.ServiceAccountAuthentication.Value.Account, + options.ServiceAccountAuthentication.Value.AuthOptions)) + : new HttpClient(); + + httpClient.DefaultRequestHeaders.Add(ZitadelDefaults.ZitadelOrgIdHeader, options.Organization); + + if (options.Token != null) + { + httpClient.DefaultRequestHeaders.Authorization = new("Bearer", options.Token); + } + + if (options.AdditionalHeaders != null) + { + foreach (var (name, value) in options.AdditionalHeaders) + { + httpClient.DefaultRequestHeaders.Add(name, value); + } + } + + var channel = GrpcChannel.ForAddress( + options.Endpoint, + new GrpcChannelOptions { HttpClient = httpClient }); + var serviceType = typeof(TClient); + + return Activator.CreateInstance(serviceType, channel) as TClient ?? + throw new($"Could not instantiate type {serviceType}"); + } + } +} diff --git a/src/Zitadel.Api/ServiceAccountHttpHandler.cs b/src/Zitadel.Api/ServiceAccountHttpHandler.cs new file mode 100644 index 00000000..f995afc5 --- /dev/null +++ b/src/Zitadel.Api/ServiceAccountHttpHandler.cs @@ -0,0 +1,49 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Zitadel.Authentication.Credentials; + +namespace Zitadel.Api +{ + internal class ServiceAccountHttpHandler : DelegatingHandler + { + private static readonly TimeSpan ServiceTokenLifetime = TimeSpan.FromHours(12); + + private readonly ServiceAccount _account; + private readonly ServiceAccount.AuthOptions _options; + + private DateTime _tokenExpiryDate; + private string? _token; + + public ServiceAccountHttpHandler(ServiceAccount account, ServiceAccount.AuthOptions options) + : base(new HttpClientHandler()) + { + _account = account; + _options = options; + } + + protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + => SendAsync(request, cancellationToken).Result; + + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + if (request.Headers.Authorization != null) + { + return await base.SendAsync(request, cancellationToken); + } + + // When the token is not fetched or it is expired, re-fetch a service account token. + if (_token == null || _tokenExpiryDate < DateTime.UtcNow) + { + _token = await _account.AuthenticateAsync(_options); + _tokenExpiryDate = DateTime.UtcNow + ServiceTokenLifetime; + } + + request.Headers.Authorization = new("Bearer", _token); + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/src/Zitadel.Api/Zitadel.Api.csproj b/src/Zitadel.Api/Zitadel.Api.csproj new file mode 100644 index 00000000..17bd043e --- /dev/null +++ b/src/Zitadel.Api/Zitadel.Api.csproj @@ -0,0 +1,85 @@ + + + + + net5.0 + + + + true + Christoph Bühler + Zitadel API + Zitadel.Api + Zitadel;API;gRPC + https://github.com/caos/zitadel-net + + The API library for Zitadel. Implemented with gRPC, + it allows access to the API of any Zitadel instance (default: + https://api.zitadel.ch). + + https://github.com/caos/zitadel-net.git + git + Apache-2.0 + icon.png + + + + + true + \ + false + + + + + + + + + + + + + + + + + + + https://raw.githubusercontent.com/caos/zitadel/master/pkg/grpc/admin/proto/admin.proto + + + https://raw.githubusercontent.com/caos/zitadel/master/pkg/grpc/auth/proto/auth.proto + + + https://raw.githubusercontent.com/caos/zitadel/master/pkg/grpc/management/proto/management.proto + + + https://raw.githubusercontent.com/caos/zitadel/master/pkg/grpc/message/proto/message.proto + + + https://raw.githubusercontent.com/caos/zitadel/master/internal/protoc/protoc-gen-authoption/authoption/options.proto + + + https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto + + + https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto + + + https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v1/protoc-gen-swagger/options/annotations.proto + + + https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v1/protoc-gen-swagger/options/openapiv2.proto + + + https://raw.githubusercontent.com/envoyproxy/protoc-gen-validate/main/validate/validate.proto + + + + + + + + + diff --git a/src/Zitadel.Api/authoption/options.proto b/src/Zitadel.Api/authoption/options.proto new file mode 100644 index 00000000..0d86e810 --- /dev/null +++ b/src/Zitadel.Api/authoption/options.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package caos.zitadel.utils.v1; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/caos/zitadel/internal/protoc/protoc-gen-authoption/authoption"; + + +extend google.protobuf.MethodOptions { + AuthOption auth_option = 50000; +} + +message AuthOption { + string permission = 1; + string check_field_name = 2; +} \ No newline at end of file diff --git a/src/Zitadel.Api/google/api/annotations.proto b/src/Zitadel.Api/google/api/annotations.proto new file mode 100644 index 00000000..85c361b4 --- /dev/null +++ b/src/Zitadel.Api/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/src/Zitadel.Api/google/api/http.proto b/src/Zitadel.Api/google/api/http.proto new file mode 100644 index 00000000..69460cf7 --- /dev/null +++ b/src/Zitadel.Api/google/api/http.proto @@ -0,0 +1,375 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/src/Zitadel.Api/icon.png b/src/Zitadel.Api/icon.png new file mode 100644 index 00000000..ac29f398 Binary files /dev/null and b/src/Zitadel.Api/icon.png differ diff --git a/src/Zitadel.Api/proto/admin.proto b/src/Zitadel.Api/proto/admin.proto new file mode 100644 index 00000000..5908f9fb --- /dev/null +++ b/src/Zitadel.Api/proto/admin.proto @@ -0,0 +1,1251 @@ + +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "validate/validate.proto"; +import "protoc-gen-swagger/options/annotations.proto"; +import "authoption/options.proto"; + +package caos.zitadel.admin.api.v1; + +option go_package ="github.com/caos/zitadel/pkg/grpc/admin"; + +option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { + info: { + title: "admin service"; + version: "0.1"; + contact:{ + url: "https://github.com/caos/zitadel/pkg/admin" + }; + }; + + schemes: HTTPS; + + consumes: "application/json"; + consumes: "application/grpc"; + + produces: "application/json"; + produces: "application/grpc"; +}; + +service AdminService { + // --------- + // Probes + // --------- + + // Healthz returns status OK as soon as the service started + rpc Healthz(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/healthz" + }; + } + +//ORG + rpc IsOrgUnique(UniqueOrgRequest) returns (UniqueOrgResponse) { + option (google.api.http) = { + get: "/orgs/_isunique" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.read" + }; + } + + rpc GetOrgByID(OrgID) returns (Org) { + option (google.api.http) = { + get: "/orgs/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.read" + }; + } + + rpc SearchOrgs(OrgSearchRequest) returns (OrgSearchResponse) { + option (google.api.http) = { + post: "/orgs/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.read" + }; + } + + rpc SetUpOrg(OrgSetUpRequest) returns (OrgSetUpResponse) { + option (google.api.http) = { + post: "/orgs/_setup" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.write" + }; + } + + //ORG_IAM_POLICY + rpc GetDefaultOrgIamPolicy(google.protobuf.Empty) returns (OrgIamPolicyView) { + option (google.api.http) = { + get: "/orgs/default/policies/orgiam" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultOrgIamPolicy(OrgIamPolicyRequest) returns (OrgIamPolicy) { + option (google.api.http) = { + put: "/orgs/default/policies/orgiam" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetOrgIamPolicy(OrgIamPolicyID) returns (OrgIamPolicyView) { + option (google.api.http) = { + get: "/orgs/{org_id}/policies/orgiam" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc CreateOrgIamPolicy(OrgIamPolicyRequest) returns (OrgIamPolicy) { + option (google.api.http) = { + post: "/orgs/{org_id}/policies/orgiam" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc UpdateOrgIamPolicy(OrgIamPolicyRequest) returns (OrgIamPolicy) { + option (google.api.http) = { + put: "/orgs/{org_id}/policies/orgiam" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc RemoveOrgIamPolicy(OrgIamPolicyID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/{org_id}/policies/orgiam" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.delete" + }; + } + + rpc GetIamMemberRoles(google.protobuf.Empty) returns (IamMemberRoles) { + option (google.api.http) = { + get: "/members/roles" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.member.read" + }; + } + + rpc AddIamMember(AddIamMemberRequest) returns (IamMember) { + option (google.api.http) = { + post: "/members" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.member.write" + }; + } + + rpc ChangeIamMember(ChangeIamMemberRequest) returns (IamMember) { + option (google.api.http) = { + put: "/members/{user_id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.member.write" + }; + } + + rpc RemoveIamMember(RemoveIamMemberRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/members/{user_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.member.delete" + }; + } + + rpc SearchIamMembers(IamMemberSearchRequest) returns (IamMemberSearchResponse) { + option (google.api.http) = { + post: "/members/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.member.read" + }; + } + + rpc GetViews(google.protobuf.Empty) returns (Views) { + option (google.api.http) = { + get: "/views" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.read" + }; + } + + rpc ClearView(ViewID) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/views/{database}/{view_name}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.write" + }; + } + + rpc GetFailedEvents(google.protobuf.Empty) returns (FailedEvents) { + option (google.api.http) = { + get: "/failedevents" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.read" + }; + } + + rpc RemoveFailedEvent(FailedEventID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/failedevents/{database}/{view_name}/{failed_sequence}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.write" + }; + } + + rpc IdpByID(IdpID) returns (IdpView) { + option (google.api.http) = { + get: "/idps/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.read" + }; + } + + rpc CreateOidcIdp(OidcIdpConfigCreate) returns (Idp) { + option (google.api.http) = { + post: "/idps/oidc" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + rpc UpdateIdpConfig(IdpUpdate) returns (Idp) { + option (google.api.http) = { + put: "/idps/{id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + rpc DeactivateIdpConfig(IdpID) returns (Idp) { + option (google.api.http) = { + put: "/idps/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + rpc ReactivateIdpConfig(IdpID) returns (Idp) { + option (google.api.http) = { + put: "/idps/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + rpc RemoveIdpConfig(IdpID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/idps/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + rpc UpdateOidcIdpConfig(OidcIdpConfigUpdate) returns (OidcIdpConfig) { + option (google.api.http) = { + put: "/idps/{idp_id}/oidcconfig" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + rpc SearchIdps(IdpSearchRequest) returns (IdpSearchResponse) { + option (google.api.http) = { + post: "/idps/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.idp.read" + }; + } + + rpc GetDefaultLabelPolicy(google.protobuf.Empty) returns (DefaultLabelPolicyView) { + option (google.api.http) = { + get: "/policies/label" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultLabelPolicy(DefaultLabelPolicyUpdate) returns (DefaultLabelPolicy) { + option (google.api.http) = { + put: "/policies/label" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultMailTemplate(google.protobuf.Empty) returns (DefaultMailTemplateView) { + option (google.api.http) = { + get: "/policies/mailtemplate" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultMailTemplate(DefaultMailTemplateUpdate) returns (DefaultMailTemplate) { + option (google.api.http) = { + put: "/policies/mailtemplate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultMailTexts(google.protobuf.Empty) returns (DefaultMailTextsView) { + option (google.api.http) = { + get: "/policies/mailtexts" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultMailText(DefaultMailTextUpdate) returns (DefaultMailText) { + option (google.api.http) = { + put: "/policies/mailtext" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultLoginPolicy(google.protobuf.Empty) returns (DefaultLoginPolicyView) { + option (google.api.http) = { + get: "/policies/login" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultLoginPolicy(DefaultLoginPolicyRequest) returns (DefaultLoginPolicy) { + option (google.api.http) = { + put: "/policies/login" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultLoginPolicyIdpProviders(IdpProviderSearchRequest) returns (IdpProviderSearchResponse) { + option (google.api.http) = { + post: "/policies/login/idpproviders/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc AddIdpProviderToDefaultLoginPolicy(IdpProviderID) returns (IdpProviderID) { + option (google.api.http) = { + post: "/policies/login/idpproviders" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc RemoveIdpProviderFromDefaultLoginPolicy(IdpProviderID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/policies/login/idpproviders/{idp_config_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultLoginPolicySecondFactors(google.protobuf.Empty) returns (SecondFactorsResult) { + option (google.api.http) = { + get: "/policies/login/secondfactors/_search" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc AddSecondFactorToDefaultLoginPolicy(SecondFactor) returns (SecondFactor) { + option (google.api.http) = { + post: "/policies/login/secondfactors" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc RemoveSecondFactorFromDefaultLoginPolicy(SecondFactor) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/policies/login/secondfactors/{second_factor}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultLoginPolicyMultiFactors(google.protobuf.Empty) returns (MultiFactorsResult) { + option (google.api.http) = { + get: "/policies/login/multifactors/_search" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc AddMultiFactorToDefaultLoginPolicy(MultiFactor) returns (MultiFactor) { + option (google.api.http) = { + post: "/policies/login/multifactors" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc RemoveMultiFactorFromDefaultLoginPolicy(MultiFactor) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/policies/login/multifactors/{multi_factor}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultPasswordComplexityPolicy(google.protobuf.Empty) returns (DefaultPasswordComplexityPolicyView) { + option (google.api.http) = { + get: "/policies/password/complexity" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultPasswordComplexityPolicy(DefaultPasswordComplexityPolicyRequest) returns (DefaultPasswordComplexityPolicy) { + option (google.api.http) = { + put: "/policies/password/complexity" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultPasswordAgePolicy(google.protobuf.Empty) returns (DefaultPasswordAgePolicyView) { + option (google.api.http) = { + get: "/policies/password/age" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultPasswordAgePolicy(DefaultPasswordAgePolicyRequest) returns (DefaultPasswordAgePolicy) { + option (google.api.http) = { + put: "/policies/password/age" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } + + rpc GetDefaultPasswordLockoutPolicy(google.protobuf.Empty) returns (DefaultPasswordLockoutPolicyView) { + option (google.api.http) = { + get: "/policies/password/lockout" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.read" + }; + } + + rpc UpdateDefaultPasswordLockoutPolicy(DefaultPasswordLockoutPolicyRequest) returns (DefaultPasswordLockoutPolicy) { + option (google.api.http) = { + put: "/policies/password/lockout" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "iam.policy.write" + }; + } +} + +message OrgID { + string id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message UniqueOrgRequest { + string name = 1 [(validate.rules).string.min_len = 1]; + string domain = 2 [(validate.rules).string.min_len = 1]; +} + +message UniqueOrgResponse { + bool is_unique = 1; +} + +message Org { + string id = 1; + OrgState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + string domain = 6; +} + +enum OrgState { + ORGSTATE_UNSPECIFIED = 0; + ORGSTATE_ACTIVE = 1; + ORGSTATE_INACTIVE = 2; +} + +message OrgSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + OrgSearchKey sorting_column = 3 [(validate.rules).enum = {not_in: [0]}];; + bool asc = 4; + repeated OrgSearchQuery queries = 5; +} + +message OrgSearchQuery { + OrgSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}];; + OrgSearchMethod method = 2; + string value = 3; +} + +enum OrgSearchKey { + ORGSEARCHKEY_UNSPECIFIED = 0; + ORGSEARCHKEY_NAME = 1; + ORGSEARCHKEY_DOMAIN = 2; + ORGSEARCHKEY_STATE = 3; +} + +message OrgSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated Org result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +enum OrgSearchMethod { + ORGSEARCHMETHOD_EQUALS = 0; + ORGSEARCHMETHOD_STARTS_WITH = 1; + ORGSEARCHMETHOD_CONTAINS = 2; +} + +message OrgSetUpRequest { + CreateOrgRequest org = 1 [(validate.rules).message.required = true]; + CreateUserRequest user = 2 [(validate.rules).message.required = true]; +} + +message OrgSetUpResponse { + Org org = 1; + UserResponse user = 2; +} + +message CreateUserRequest { + string user_name = 1 [(validate.rules).string.pattern = "^[^[:space:]]{1,200}$"]; + + oneof user { + option (validate.required) = true; + + CreateHumanRequest human = 2; + CreateMachineRequest machine = 3; + } +} + +message CreateHumanRequest { + string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string nick_name = 3 [(validate.rules).string = {max_len: 200}]; + string preferred_language = 4 [(validate.rules).string = {max_len: 200}]; + Gender gender = 5; + string email = 6 [(validate.rules).string = {min_len: 1, max_len: 200, email: true}]; + bool is_email_verified = 7; + string phone = 8 [(validate.rules).string = {max_len: 20}]; + bool is_phone_verified = 9; + string country = 10 [(validate.rules).string = {max_len: 200}]; + string locality = 11 [(validate.rules).string = {max_len: 200}]; + string postal_code = 12 [(validate.rules).string = {max_len: 200}]; + string region = 13 [(validate.rules).string = {max_len: 200}]; + string street_address = 14 [(validate.rules).string = {max_len: 200}]; + string password = 15 [(validate.rules).string = {max_len: 72}]; +} + +message CreateMachineRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string description = 2 [(validate.rules).string = {max_len: 500}]; + } + +message UserResponse { + string id = 1; + UserState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + uint64 sequence = 5; + string user_name = 6; + + oneof user { + option (validate.required) = true; + + HumanResponse human = 7; + MachineResponse machine = 8; + } +} + +enum UserState { + USERSTATE_UNSPECIFIED = 0; + USERSTATE_ACTIVE = 1; + USERSTATE_INACTIVE = 2; + USERSTATE_DELETED = 3; + USERSTATE_LOCKED = 4; + USERSTATE_SUSPEND = 5; + USERSTATE_INITIAL= 6; +} + +enum Gender { + GENDER_UNSPECIFIED = 0; + GENDER_FEMALE = 1; + GENDER_MALE = 2; + GENDER_DIVERSE = 3; +} + +message HumanResponse { + string first_name = 1; + string last_name = 2; + string display_name = 3; + string nick_name = 4; + string preferred_language = 5; + Gender gender = 6; + string email = 7; + bool is_email_verified = 8; + string phone = 9; + bool is_phone_verified = 10; + string country = 11; + string locality = 12; + string postal_code = 13; + string region = 14; + string street_address = 15; + } + + message MachineResponse { + string name = 1; + string description = 2; + repeated MachineKeyResponse keys = 3; + } + + message MachineKeyResponse { + string id = 1; + MachineKeyType type = 2; + uint64 sequence = 3; + + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp expiration_date = 5; + } + + enum MachineKeyType { + MACHINEKEY_UNSPECIFIED = 0; + MACHINEKEY_JSON = 1; + } + +message CreateOrgRequest { + string name = 1 [(validate.rules).string.min_len = 1]; + string domain = 2; +} + +message OrgIamPolicy { + string org_id = 1; + bool user_login_must_be_domain = 2; + bool default = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message OrgIamPolicyView { + string org_id = 1; + bool user_login_must_be_domain = 2; + bool default = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message OrgIamPolicyRequest { + string org_id = 1 [(validate.rules).string = {min_len: 1}]; + string description = 2; + bool user_login_must_be_domain = 3; +} + +message OrgIamPolicyID { + string org_id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message IamMemberRoles { + repeated string roles = 1; +} + +message IamMember { + string user_id = 1; + repeated string roles = 2; + google.protobuf.Timestamp change_date = 3; + google.protobuf.Timestamp creation_date = 4; + uint64 sequence = 5; +} + +message AddIamMemberRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 2; +} + +message ChangeIamMemberRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 2; +} + +message RemoveIamMemberRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message IamMemberSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated IamMemberView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message IamMemberView { + string user_id = 1; + repeated string roles = 2; + google.protobuf.Timestamp change_date = 3; + google.protobuf.Timestamp creation_date = 4; + uint64 sequence = 5; + string user_name = 6; + string email = 7; + string first_name = 8; + string last_name = 9; + string display_name = 10; +} + +message IamMemberSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated IamMemberSearchQuery queries = 3; +} + +message IamMemberSearchQuery { + IamMemberSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum IamMemberSearchKey { + IAMMEMBERSEARCHKEY_UNSPECIFIED = 0; + IAMMEMBERSEARCHKEY_FIRST_NAME = 1; + IAMMEMBERSEARCHKEY_LAST_NAME = 2; + IAMMEMBERSEARCHKEY_EMAIL = 3; + IAMMEMBERSEARCHKEY_USER_ID = 4; +} + +enum SearchMethod { + SEARCHMETHOD_EQUALS = 0; + SEARCHMETHOD_STARTS_WITH = 1; + SEARCHMETHOD_CONTAINS = 2; + SEARCHMETHOD_EQUALS_IGNORE_CASE = 3; + SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4; + SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5; + SEARCHMETHOD_NOT_EQUALS = 6; + SEARCHMETHOD_GREATER_THAN = 7; + SEARCHMETHOD_LESS_THAN = 8; + SEARCHMETHOD_IS_ONE_OF = 9; + SEARCHMETHOD_LIST_CONTAINS = 10; +} + +message FailedEventID { + string database = 1 [(validate.rules).string = {min_len: 1}]; + string view_name = 2 [(validate.rules).string = {min_len: 1}]; + uint64 failed_sequence = 3; +} + +message FailedEvents { + repeated FailedEvent failed_events = 1; +} + +message FailedEvent { + string database = 1; + string view_name = 2; + uint64 failed_sequence = 3; + uint64 failure_count = 4; + string error_message = 5; +} + +message ViewID { + string database = 1 [(validate.rules).string = {min_len: 1}]; + string view_name = 2 [(validate.rules).string = {min_len: 1}]; +} + +message Views { + repeated View views = 1; +} + +message View { + string database = 1; + string view_name = 2; + uint64 processed_sequence = 3; + google.protobuf.Timestamp event_timestamp = 4; + google.protobuf.Timestamp last_successful_spooler_run = 5; +} + +message IdpID { + string id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message Idp { + string id = 1; + IdpState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + IdpStylingType styling_type = 6; + oneof idp_config { + OidcIdpConfig oidc_config = 7; + } + uint64 sequence = 8; +} + +message IdpUpdate { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string name = 2; + IdpStylingType styling_type = 3; +} + +message OidcIdpConfig { + string client_id = 1; + string client_secret = 2; + string issuer = 3; + repeated string scopes = 4; +} + +enum IdpStylingType { + IDPSTYLINGTYPE_UNSPECIFIED = 0; + IDPSTYLINGTYPE_GOOGLE = 1; +} + +enum IdpState { + IDPCONFIGSTATE_UNSPECIFIED = 0; + IDPCONFIGSTATE_ACTIVE = 1; + IDPCONFIGSTATE_INACTIVE = 2; +} + +enum OIDCMappingField { + OIDCMAPPINGFIELD_UNSPECIFIED = 0; + OIDCMAPPINGFIELD_PREFERRED_USERNAME = 1; + OIDCMAPPINGFIELD_EMAIL = 2; +} + +message OidcIdpConfigCreate { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + IdpStylingType styling_type = 2; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 6; + OIDCMappingField idp_display_name_mapping = 7; + OIDCMappingField username_mapping = 8; +} + +message OidcIdpConfigUpdate { + string idp_id = 1 [(validate.rules).string = {min_len: 1}]; + string client_id = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 3; + string issuer = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 5; + OIDCMappingField idp_display_name_mapping = 6; + OIDCMappingField username_mapping = 7; +} + +message IdpSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated IdpView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message IdpView { + string id = 1; + IdpState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + IdpStylingType styling_type = 6; + oneof idp_config_view { + OidcIdpConfigView oidc_config = 7; + } + uint64 sequence = 8; +} + +message OidcIdpConfigView { + string client_id = 1; + string issuer = 2; + repeated string scopes = 3; + OIDCMappingField idp_display_name_mapping = 4; + OIDCMappingField username_mapping = 5; +} + +message IdpSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated IdpSearchQuery queries = 3; +} + +message IdpSearchQuery { + IdpSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum IdpSearchKey { + IDPSEARCHKEY_UNSPECIFIED = 0; + IDPSEARCHKEY_IDP_CONFIG_ID = 1; + IDPSEARCHKEY_NAME = 2; +} + +message DefaultLabelPolicy { + string primary_color = 1; + string secondary_color = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + bool hide_login_name_suffix = 5; +} + +message DefaultLabelPolicyUpdate { + string primary_color = 1; + string secondary_color = 2; + bool hide_login_name_suffix = 3; +} + +message DefaultLabelPolicyView { + string primary_color = 1; + string secondary_color = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + bool hide_login_name_suffix = 5; +} + +message DefaultMailTemplate { + bytes template = 1; + google.protobuf.Timestamp creation_date = 2; + google.protobuf.Timestamp change_date = 3; +} + +message DefaultMailTemplateUpdate { + bytes template = 1; +} + +message DefaultMailTemplateView { + bytes template = 1; + google.protobuf.Timestamp creation_date = 2; + google.protobuf.Timestamp change_date = 3; +} + +message DefaultMailText { + string mail_text_type = 1; + string language = 2; + string title = 3; + string pre_header = 4; + string subject = 5; + string greeting = 6; + string text = 7; + string button_text = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; +} + +message DefaultMailTextUpdate { + string mail_text_type = 1; + string language = 2; + string title = 3; + string pre_header = 4; + string subject = 5; + string greeting = 6; + string text = 7; + string button_text = 8; +} + +message DefaultMailTextsView{ + repeated DefaultMailTextView texts = 1; +} + +message DefaultMailTextView { + string mail_text_type = 1; + string language = 2; + string title = 3; + string pre_header = 4; + string subject = 5; + string greeting = 6; + string text = 7; + string button_text = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; +} + +message DefaultLoginPolicy { + bool allow_username_password = 1; + bool allow_register = 2; + bool allow_external_idp = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; + bool force_mfa = 6; + PasswordlessType passwordless_type = 7; +} + +message DefaultLoginPolicyRequest { + bool allow_username_password = 1; + bool allow_register = 2; + bool allow_external_idp = 3; + bool force_mfa = 4; + PasswordlessType passwordless_type = 5; +} + +enum PasswordlessType { + PASSWORDLESSTYPE_NOT_ALLOWED = 0; + PASSWORDLESSTYPE_ALLOWED = 1; +} + +message IdpProviderID { + string idp_config_id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message DefaultLoginPolicyView { + bool allow_username_password = 1; + bool allow_register = 2; + bool allow_external_idp = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; + bool force_mfa = 6; + PasswordlessType passwordless_type = 7; +} + +message IdpProviderView { + string idp_config_id = 1; + string name = 2; + IdpType type = 3; +} + +enum IdpType { + IDPTYPE_UNSPECIFIED = 0; + IDPTYPE_OIDC = 1; + IDPTYPE_SAML = 2; +} + +message IdpProviderSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated IdpProviderView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message IdpProviderSearchRequest { + uint64 offset = 1; + uint64 limit = 2; +} + +message SecondFactorsResult { + repeated SecondFactorType second_factors = 1; +} + +message SecondFactor { + SecondFactorType second_factor = 1; +} + +enum SecondFactorType { + SECONDFACTORTYPE_UNSPECIFIED = 0; + SECONDFACTORTYPE_OTP = 1; + SECONDFACTORTYPE_U2F = 2; +} + +message MultiFactorsResult { + repeated MultiFactorType multi_factors = 1; +} + +message MultiFactor { + MultiFactorType multi_factor = 1; +} + +enum MultiFactorType { + MULTIFACTORTYPE_UNSPECIFIED = 0; + MULTIFACTORTYPE_U2F_WITH_PIN = 1; +} + +message DefaultPasswordComplexityPolicy { + uint64 min_length = 1; + bool has_uppercase = 2; + bool has_lowercase = 3; + bool has_number = 4; + bool has_symbol = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; +} + +message DefaultPasswordComplexityPolicyRequest { + uint64 min_length = 1; + bool has_uppercase = 2; + bool has_lowercase = 3; + bool has_number = 4; + bool has_symbol = 5; +} + +message DefaultPasswordComplexityPolicyView { + uint64 min_length = 1; + bool has_uppercase = 2; + bool has_lowercase = 3; + bool has_number = 4; + bool has_symbol = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; +} + +message DefaultPasswordAgePolicy { + uint64 max_age_days = 1; + uint64 expire_warn_days = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; +} + +message DefaultPasswordAgePolicyRequest { + uint64 max_age_days = 1; + uint64 expire_warn_days = 2; +} + +message DefaultPasswordAgePolicyView { + uint64 max_age_days = 1; + uint64 expire_warn_days = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; +} + +message DefaultPasswordLockoutPolicy { + uint64 max_attempts = 1; + bool show_lockout_failure = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; +} + +message DefaultPasswordLockoutPolicyRequest { + uint64 max_attempts = 1; + bool show_lockout_failure = 2; +} + +message DefaultPasswordLockoutPolicyView { + uint64 max_attempts = 1; + bool show_lockout_failure = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; +} \ No newline at end of file diff --git a/src/Zitadel.Api/proto/auth.proto b/src/Zitadel.Api/proto/auth.proto new file mode 100644 index 00000000..bb5ca291 --- /dev/null +++ b/src/Zitadel.Api/proto/auth.proto @@ -0,0 +1,918 @@ +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "validate/validate.proto"; +import "protoc-gen-swagger/options/annotations.proto"; +import "authoption/options.proto"; +import "proto/message.proto"; + +package caos.zitadel.auth.api.v1; + +option go_package = "github.com/caos/zitadel/pkg/grpc/auth"; + +option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { + info: { + title: "Auth API"; + version: "0.1"; + contact:{ + url: "https://github.com/caos/zitadel/pkg/auth" + }; + }; + + schemes: HTTPS; + + consumes: "application/json"; + consumes: "application/grpc"; + + produces: "application/json"; + produces: "application/grpc"; +}; + +service AuthService { + // Readiness + rpc Healthz(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/healthz" + }; + } + + // Authorization + rpc GetMyUserSessions(google.protobuf.Empty) returns (UserSessionViews) { + option (google.api.http) = { + get: "/users/me/sessions" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + //User + rpc GetMyUser(google.protobuf.Empty) returns (UserView) { + option (google.api.http) = { + get: "/users/me" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfileView) { + option (google.api.http) = { + get: "/users/me/profile" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc UpdateMyUserProfile(UpdateUserProfileRequest) returns (UserProfile) { + option (google.api.http) = { + put: "/users/me/profile" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc ChangeMyUserName(ChangeUserNameRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/users/me/username" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyUserEmail(google.protobuf.Empty) returns (UserEmailView) { + option (google.api.http) = { + get: "/users/me/email" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc ChangeMyUserEmail(UpdateUserEmailRequest) returns (UserEmail) { + option (google.api.http) = { + put: "/users/me/email" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc VerifyMyUserEmail(VerifyMyUserEmailRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/me/email/_verify" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc ResendMyEmailVerificationMail(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/me/email/_resendverification" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyUserPhone(google.protobuf.Empty) returns (UserPhoneView) { + option (google.api.http) = { + get: "/users/me/phone" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc ChangeMyUserPhone(UpdateUserPhoneRequest) returns (UserPhone) { + option (google.api.http) = { + put: "/users/me/phone" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc RemoveMyUserPhone(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/me/phone" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc VerifyMyUserPhone(VerifyUserPhoneRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/me/phone/_verify" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc ResendMyPhoneVerificationCode(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/me/phone/_resendverification" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyUserAddress(google.protobuf.Empty) returns (UserAddressView) { + option (google.api.http) = { + get: "/users/me/address" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyUserChanges(ChangesRequest) returns (Changes) { + option (google.api.http) = { + get: "/users/me/changes" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc UpdateMyUserAddress(UpdateUserAddressRequest) returns (UserAddress) { + option (google.api.http) = { + put: "/users/me/address" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyMfas(google.protobuf.Empty) returns (MultiFactors) { + option (google.api.http) = { + get: "/users/me/mfas" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + //Password + rpc ChangeMyPassword(PasswordChange) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/users/me/passwords/_change" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyPasswordComplexityPolicy(google.protobuf.Empty) returns (PasswordComplexityPolicy) { + option (google.api.http) = { + get: "/policies/passwords/complexity" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + //ExternalIDP + rpc SearchMyExternalIDPs(ExternalIDPSearchRequest) returns (ExternalIDPSearchResponse) { + option (google.api.http) = { + post: "/users/me/externalidps/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc RemoveMyExternalIDP(ExternalIDPRemoveRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/me/externalidps/{idp_config_id}/{external_user_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + // MFA + rpc AddMfaOTP(google.protobuf.Empty) returns (MfaOtpResponse) { + option (google.api.http) = { + post: "/users/me/mfas/otp" + body: "*" + }; + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc VerifyMfaOTP(VerifyMfaOtp) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/users/me/mfas/otp/_verify" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc RemoveMfaOTP(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/me/mfas/otp" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc AddMyMfaU2F(google.protobuf.Empty) returns (WebAuthNResponse) { + option (google.api.http) = { + post: "/users/me/mfas/u2f" + body: "*" + }; + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc VerifyMyMfaU2F(VerifyWebAuthN) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/users/me/mfas/u2f/_verify" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc RemoveMyMfaU2F(WebAuthNTokenID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/me/mfas/u2f/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyPasswordless(google.protobuf.Empty) returns (WebAuthNTokens) { + option (google.api.http) = { + get: "/users/me/passwordless" + }; + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc AddMyPasswordless(google.protobuf.Empty) returns (WebAuthNResponse) { + option (google.api.http) = { + post: "/users/me/passwordless" + body: "*" + }; + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc VerifyMyPasswordless(VerifyWebAuthN) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/users/me/passwordless/_verify" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc RemoveMyPasswordless(WebAuthNTokenID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/me/passwordless/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc SearchMyUserGrant(UserGrantSearchRequest) returns (UserGrantSearchResponse) { + option (google.api.http) = { + post: "/usergrants/me/_search" + body: "*" + }; + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc SearchMyProjectOrgs(MyProjectOrgSearchRequest) returns (MyProjectOrgSearchResponse) { + option (google.api.http) = { + post: "/global/projectorgs/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + //Permission + rpc GetMyZitadelPermissions(google.protobuf.Empty) returns (MyPermissions) { + option (google.api.http) = { + get: "/permissions/zitadel/me" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetMyProjectPermissions(google.protobuf.Empty) returns (MyPermissions) { + option (google.api.http) = { + get: "/permissions/me" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc SearchMyUserMemberships(UserMembershipSearchRequest) returns (UserMembershipSearchResponse) { + option (google.api.http) = { + post: "/users/me/memberships/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } +} + +message UserSessionViews { + repeated UserSessionView user_sessions = 1; +} + +message UserSessionView { + string id = 1; + string agent_id = 2; + UserSessionState auth_state = 3; + string user_id = 4; + string user_name = 5; + uint64 sequence = 6; + string login_name = 7; + string display_name = 8; +} + +enum UserSessionState { + USERSESSIONSTATE_UNSPECIFIED = 0; + USERSESSIONSTATE_ACTIVE = 1; + USERSESSIONSTATE_TERMINATED = 2; +} + +message UserView { + string id = 1; + UserState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + uint64 sequence = 5; + repeated string login_names = 6; + string preferred_login_name = 7; + google.protobuf.Timestamp last_login = 8; + string resource_owner = 9; + string user_name = 10; + + oneof user { + option (validate.required) = true; + + HumanView human = 11; + MachineView machine = 12; + } +} + +message MachineView { + google.protobuf.Timestamp last_key_added = 1; + + string name = 2; + string description = 3; +} + +message MachineKeyView { + string id = 1; + MachineKeyType type = 2; + uint64 sequence = 3; + + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp expiration_date = 5; +} + +enum MachineKeyType { + MACHINEKEY_UNSPECIFIED = 0; + MACHINEKEY_JSON = 1; +} + +message HumanView { + google.protobuf.Timestamp password_changed = 1; + string first_name = 2; + string last_name = 3; + string display_name = 4; + string nick_name = 5; + string preferred_language = 6; + Gender gender = 7; + string email = 8; + bool is_email_verified = 9; + string phone = 10; + bool is_phone_verified = 11; + string country = 12; + string locality = 13; + string postal_code = 14; + string region = 15; + string street_address = 16; +} + +enum UserState { + USERSTATE_UNSPECIFIED = 0; + USERSTATE_ACTIVE = 1; + USERSTATE_INACTIVE = 2; + USERSTATE_DELETED = 3; + USERSTATE_LOCKED = 4; + USERSTATE_SUSPEND = 5; + USERSTATE_INITIAL = 6; +} + +enum Gender { + GENDER_UNSPECIFIED = 0; + GENDER_FEMALE = 1; + GENDER_MALE = 2; + GENDER_DIVERSE = 3; +} + +message UserProfile { + string id = 1; + string first_name = 2; + string last_name = 3; + string nick_name = 4; + string display_name = 5; + string preferred_language = 6; + Gender gender = 7; + uint64 sequence = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; +} + +message UserProfileView { + string id = 1; + string first_name = 2; + string last_name = 3; + string nick_name = 4; + string display_name = 5; + string preferred_language = 6; + Gender gender = 7; + uint64 sequence = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; + repeated string login_names = 11; + string preferred_login_name = 12; +} + +message UpdateUserProfileRequest { + string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string nick_name = 3 [(validate.rules).string.max_len = 200]; + string preferred_language = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + Gender gender = 5; +} + +message ChangeUserNameRequest { + string user_name = 1 [(validate.rules).string.pattern = "^[^[:space:]]{1,200}$"]; +} + +message UserEmail { + string id = 1; + string email = 2; + bool isEmailVerified = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message UserEmailView { + string id = 1; + string email = 2; + bool isEmailVerified = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message VerifyMyUserEmailRequest { + string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message UpdateUserEmailRequest { + string email = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message UserPhone { + string id = 1; + string phone = 2; + bool is_phone_verified = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message UserPhoneView { + string id = 1; + string phone = 2; + bool is_phone_verified = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message UpdateUserPhoneRequest { + string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 20}]; +} + +message VerifyUserPhoneRequest { + string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message UserAddress { + string id = 1; + string country = 2; + string locality = 3; + string postal_code = 4; + string region = 5; + string street_address = 6; + uint64 sequence = 7; + google.protobuf.Timestamp creation_date = 8; + google.protobuf.Timestamp change_date = 9; +} + +message UserAddressView { + string id = 1; + string country = 2; + string locality = 3; + string postal_code = 4; + string region = 5; + string street_address = 6; + uint64 sequence = 7; + google.protobuf.Timestamp creation_date = 8; + google.protobuf.Timestamp change_date = 9; +} + +message UpdateUserAddressRequest { + string country = 1 [(validate.rules).string = {max_len: 200}]; + string locality = 2 [(validate.rules).string = {max_len: 200}]; + string postal_code = 3 [(validate.rules).string = {max_len: 200}]; + string region = 4 [(validate.rules).string = {max_len: 200}]; + string street_address = 5 [(validate.rules).string = {max_len: 200}]; +} + +message PasswordChange { + string old_password = 1 [(validate.rules).string = {min_len: 1, max_len: 72}]; + string new_password = 2 [(validate.rules).string = {min_len: 1, max_len: 72}]; +} + +enum MfaType { + MFATYPE_UNSPECIFIED = 0; + MFATYPE_OTP = 1; + MFATYPE_U2F = 2; +} + +message VerifyMfaOtp { + string code = 1 [(validate.rules).string = {min_len: 1}]; +} + +message MultiFactors { + repeated MultiFactor mfas = 1; +} + +message MultiFactor { + MfaType type = 1; + MFAState state = 2; + string attribute = 3; + string id = 4; +} + +message MfaOtpResponse { + string user_id = 1; + string url = 2; + string secret = 3; + MFAState state = 4; +} + +message WebAuthNTokens { + repeated WebAuthNToken tokens = 1; +} + +message WebAuthNToken { + string id = 1; + string name = 2; + MFAState state = 3; +} + +message WebAuthNResponse { + string id = 1; + bytes public_key = 2; + MFAState state = 3; +} + +message VerifyWebAuthN { + bytes public_key_credential = 1; + string token_name = 2; +} + +message WebAuthNTokenID { + string id = 1; +} + +enum MFAState { + MFASTATE_UNSPECIFIED = 0; + MFASTATE_NOT_READY = 1; + MFASTATE_READY = 2; + MFASTATE_REMOVED = 3; +} + +message UserGrantSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + UserGrantSearchKey sorting_column = 3 [(validate.rules).enum = {not_in: [0]}];; + bool asc = 4; + repeated UserGrantSearchQuery queries = 5; +} + +message UserGrantSearchQuery { + UserGrantSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}];; + SearchMethod method = 2; + string value = 3; +} + +enum UserGrantSearchKey { + UserGrantSearchKey_UNKNOWN = 0; + UserGrantSearchKey_ORG_ID = 1; + UserGrantSearchKey_PROJECT_ID = 2; +} + +message UserGrantSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated UserGrantView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message UserGrantView { + string OrgId = 1; + string ProjectId = 2; + string UserId = 3; + repeated string Roles = 4; + string OrgName = 5; + string GrantId = 6; +} + +message MyProjectOrgSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + bool asc = 4; + repeated MyProjectOrgSearchQuery queries = 5; +} + +message MyProjectOrgSearchQuery { + MyProjectOrgSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}];; + SearchMethod method = 2; + string value = 3; +} + +enum MyProjectOrgSearchKey { + MYPROJECTORGSEARCHKEY_UNSPECIFIED = 0; + MYPROJECTORGSEARCHKEY_ORG_NAME = 1; +} + +message MyProjectOrgSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated Org result = 4; +} + +message Org { + string id = 1; + string name = 2; +} + +message MyPermissions { + repeated string permissions = 1; +} + +enum SearchMethod { + SEARCHMETHOD_EQUALS = 0; + SEARCHMETHOD_STARTS_WITH = 1; + SEARCHMETHOD_CONTAINS = 2; + SEARCHMETHOD_EQUALS_IGNORE_CASE = 3; + SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4; + SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5; +} + +message ChangesRequest { + uint64 limit = 1; + uint64 sequence_offset = 2; + bool asc = 3; +} + +message Changes { + repeated Change changes = 1; + uint64 offset = 2; + uint64 limit = 3; +} + +message Change { + google.protobuf.Timestamp change_date = 1; + caos.zitadel.api.v1.LocalizedMessage event_type = 2; + uint64 sequence = 3; + string editor_id = 4; + string editor = 5; + google.protobuf.Struct data = 6; +} + +message PasswordComplexityPolicy { + string id = 1; + string description = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + uint64 min_length = 5; + bool has_lowercase = 6; + bool has_uppercase = 7; + bool has_number = 8; + bool has_symbol = 9; + uint64 sequence = 10; + bool is_default = 11; +} + +message ExternalIDPResponse { + string idp_config_id = 1; + string user_id = 2; + string display_name = 3; +} + +message ExternalIDPRemoveRequest { + string idp_config_id = 1; + string external_user_id = 2; +} + +message ExternalIDPSearchRequest { + uint64 offset = 1; + uint64 limit = 2; +} + +message ExternalIDPSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ExternalIDPView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ExternalIDPView { + string user_id = 1; + string idp_config_id = 2; + string external_user_id = 3; + string idp_name = 4; + string external_user_display_name = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; +} + + +message UserMembershipSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated UserMembershipView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message UserMembershipSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated UserMembershipSearchQuery queries = 3; +} + +message UserMembershipSearchQuery { + UserMembershipSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2 [(validate.rules).enum = {in: [0]}]; + string value = 3; +} + +enum UserMembershipSearchKey { + USERMEMBERSHIPSEARCHKEY_UNSPECIFIED = 0; + USERMEMBERSHIPSEARCHKEY_TYPE = 1; + USERMEMBERSHIPSEARCHKEY_OBJECT_ID = 2; +} + +message UserMembershipView { + string user_id = 1; + MemberType member_type = 2; + string aggregate_id = 3; + string object_id = 4; + repeated string roles = 5; + string display_name = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; + uint64 sequence = 9; + string resource_owner = 10; +} + +enum MemberType { + MEMBERTYPE_UNSPECIFIED = 0; + MEMBERTYPE_ORGANISATION = 1; + MEMBERTYPE_PROJECT = 2; + MEMBERTYPE_PROJECT_GRANT = 3; +} diff --git a/src/Zitadel.Api/proto/management.proto b/src/Zitadel.Api/proto/management.proto new file mode 100644 index 00000000..f96c1598 --- /dev/null +++ b/src/Zitadel.Api/proto/management.proto @@ -0,0 +1,3767 @@ +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; +import "protoc-gen-swagger/options/annotations.proto"; +import "validate/validate.proto"; +import "authoption/options.proto"; +import "proto/message.proto"; + +package caos.zitadel.management.api.v1; + +option go_package = "github.com/caos/zitadel/pkg/grpc/management"; + +option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { + info: { + title: "Management API"; + version: "0.1"; + contact:{ + url: "https://github.com/caos/zitadel/pkg/management" + }; + }; + + schemes: HTTPS; + + consumes: "application/json"; + consumes: "application/grpc"; + + produces: "application/json"; + produces: "application/grpc"; +}; + +// All requests are based on your context if nothing other is specified +// Requests which have /me in the url get the parameter from the context +service ManagementService { + + //READINESS + rpc Healthz(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/healthz" + }; + } + + rpc GetZitadelDocs(google.protobuf.Empty) returns (ZitadelDocs) { + option (google.api.http) = { + get: "/zitadel/docs" + }; + } + + // GetIam returns some needed settings of the iam (Global Organisation ID, Zitadel Project ID) + rpc GetIam(google.protobuf.Empty) returns (Iam) { + option (google.api.http) = { + get: "/iam" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc IsUserUnique(UniqueUserRequest) returns (UniqueUserResponse) { + option (google.api.http) = { + get: "/users/_isunique" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc GetUserByID(UserID) returns (UserView) { + option (google.api.http) = { + get: "/users/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + // GetUserByLoginNameGlobal returns User, global search is overall organisations + rpc GetUserByLoginNameGlobal(LoginName) returns (UserView) { + option (google.api.http) = { + get: "/global/users/_byloginname" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.global.read" + }; + } + + // Limit should always be set, there is a default limit set by the service + rpc SearchUsers(UserSearchRequest) returns (UserSearchResponse) { + option (google.api.http) = { + post: "/users/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc CreateUser(CreateUserRequest) returns (UserResponse) { + option (google.api.http) = { + post: "/users" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc ImportHuman(ImportHumanRequest) returns (UserResponse) { + option (google.api.http) = { + post: "/users/humans/_import" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc DeactivateUser(UserID) returns (UserResponse) { + option (google.api.http) = { + put: "/users/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc ReactivateUser(UserID) returns (UserResponse) { + option (google.api.http) = { + put: "/users/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc LockUser(UserID) returns (UserResponse) { + option (google.api.http) = { + put: "/users/{id}/_lock" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc UnlockUser(UserID) returns (UserResponse) { + option (google.api.http) = { + put: "/users/{id}/_unlock" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc DeleteUser(UserID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.delete" + }; + } + + // UserChanges returns the event stream of the user object + rpc UserChanges(ChangeRequest) returns (Changes) { + option (google.api.http) = { + get: "/users/{id}/changes" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc AddMachineKey(AddMachineKeyRequest) returns (AddMachineKeyResponse) { + option (google.api.http) = { + post: "/users/{user_id}/keys" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc DeleteMachineKey(MachineKeyIDRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/keys/{key_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc SearchMachineKeys(MachineKeySearchRequest) returns (MachineKeySearchResponse) { + option (google.api.http) = { + post: "/users/{user_id}/keys/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc GetMachineKey(MachineKeyIDRequest) returns (MachineKeyView) { + option (google.api.http) = { + get: "/users/{user_id}/keys/{key_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc GetUserProfile(UserID) returns (UserProfileView) { + option (google.api.http) = { + get: "/users/{id}/profile" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UserProfile) { + option (google.api.http) = { + put: "/users/{id}/profile" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc GetUserEmail(UserID) returns (UserEmailView) { + option (google.api.http) = { + get: "/users/{id}/email" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc ChangeUserUserName(UpdateUserUserNameRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/users/{id}/username" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc ChangeUserEmail(UpdateUserEmailRequest) returns (UserEmail) { + option (google.api.http) = { + put: "/users/{id}/email" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc ResendEmailVerificationMail(UserID) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/{id}/email/_resendverification" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc GetUserPhone(UserID) returns (UserPhoneView) { + option (google.api.http) = { + get: "/users/{id}/phone" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc ChangeUserPhone(UpdateUserPhoneRequest) returns (UserPhone) { + option (google.api.http) = { + put: "/users/{id}/phone" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc RemoveUserPhone(UserID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{id}/phone" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc ResendPhoneVerificationCode(UserID) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/{id}/phone/_resendverification" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc GetUserAddress(UserID) returns (UserAddressView) { + option (google.api.http) = { + get: "/users/{id}/address" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc UpdateUserAddress(UpdateUserAddressRequest) returns (UserAddress) { + option (google.api.http) = { + put: "/users/{id}/address" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc UpdateUserMachine(UpdateMachineRequest) returns (MachineResponse) { + option (google.api.http) = { + put: "/users/{id}/machine" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc SearchUserExternalIDPs(ExternalIDPSearchRequest) returns (ExternalIDPSearchResponse) { + option (google.api.http) = { + post: "/users/{user_id}/externalidps/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc RemoveExternalIDP(ExternalIDPRemoveRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/externalidps/{idp_config_id}/{external_user_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc GetUserMfas(UserID) returns (UserMultiFactors) { + option (google.api.http) = { + get: "/users/{id}/mfas" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc RemoveMfaOTP(UserID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{id}/mfas/otp" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc RemoveMfaU2F(WebAuthNTokenID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/mfas/u2f/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc GetPasswordless(UserID) returns (WebAuthNTokens) { + option (google.api.http) = { + get: "/users/{id}/passwordless" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.read" + }; + } + + rpc RemovePasswordless(WebAuthNTokenID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/passwordless" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + // Sends an Notification (Email/SMS) with a password reset Link + rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/{id}/password/_sendsetnotification" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + // A Manager is only allowed to set an initial password, on the next login the user has to change his password + rpc SetInitialPassword(PasswordRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/{id}/password/_initialize" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc ResendInitialMail(InitialMailRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/{id}/_resendinitialisation" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + + rpc SearchUserMemberships(UserMembershipSearchRequest) returns (UserMembershipSearchResponse) { + option (google.api.http) = { + post: "/users/{user_id}/memberships/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.membership.read" + }; + } + + rpc CreateOrg(OrgCreateRequest) returns (Org) { + option (google.api.http) = { + post: "/orgs" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.create" + }; + } + + // OrgChanges returns the event stream of the org object + rpc OrgChanges(ChangeRequest) returns (Changes) { + option (google.api.http) = { + get: "/orgs/{id}/changes" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.read" + }; + } + + rpc GetMyOrg(google.protobuf.Empty) returns (OrgView) { + option (google.api.http) = { + get: "/orgs/me" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.read" + }; + } + + // search a organisation by its domain overall organisations + rpc GetOrgByDomainGlobal(Domain) returns (OrgView) { + option (google.api.http) = { + get: "/global/orgs/_bydomain" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.global.read" + }; + } + + rpc DeactivateMyOrg(google.protobuf.Empty) returns (Org) { + option (google.api.http) = { + put: "/orgs/me/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc ReactivateMyOrg(google.protobuf.Empty) returns (Org) { + option (google.api.http) = { + put: "/orgs/me/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc SearchMyOrgDomains(OrgDomainSearchRequest) returns (OrgDomainSearchResponse) { + option (google.api.http) = { + post: "/orgs/me/domains/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.read" + }; + } + + rpc AddMyOrgDomain(AddOrgDomainRequest) returns (OrgDomain) { + option (google.api.http) = { + post: "/orgs/me/domains" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc GenerateMyOrgDomainValidation(OrgDomainValidationRequest) returns (OrgDomainValidationResponse) { + option (google.api.http) = { + post: "/orgs/me/domains/{domain}/validation/create" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc ValidateMyOrgDomain(ValidateOrgDomainRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/orgs/me/domains/{domain}/validation/check" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc SetMyPrimaryOrgDomain(PrimaryOrgDomainRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/orgs/me/domains/{domain}/_primary" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc RemoveMyOrgDomain(RemoveOrgDomainRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/domains/{domain}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.write" + }; + } + + rpc GetMyOrgIamPolicy(google.protobuf.Empty) returns (OrgIamPolicyView) { + option (google.api.http) = { + get: "/orgs/me/iampolicy" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } + + rpc GetOrgMemberRoles(google.protobuf.Empty) returns (OrgMemberRoles) { + option (google.api.http) = { + get: "/orgs/members/roles" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.member.read" + }; + } + + rpc AddMyOrgMember(AddOrgMemberRequest) returns (OrgMember) { + option (google.api.http) = { + post: "/orgs/me/members" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.member.write" + }; + } + + rpc ChangeMyOrgMember(ChangeOrgMemberRequest) returns (OrgMember) { + option (google.api.http) = { + put: "/orgs/me/members/{user_id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.member.write" + }; + } + + rpc RemoveMyOrgMember(RemoveOrgMemberRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/members/{user_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.member.delete" + }; + } + + rpc SearchMyOrgMembers(OrgMemberSearchRequest) returns (OrgMemberSearchResponse) { + option (google.api.http) = { + post: "/orgs/me/members/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.member.read" + }; + } + + // ProjectChanges returns the event stream of the project object + rpc ProjectChanges(ChangeRequest) returns (Changes) { + option (google.api.http) = { + get: "/projects/{id}/changes" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.read" + }; + } + + rpc SearchProjects(ProjectSearchRequest) returns (ProjectSearchResponse) { + option (google.api.http) = { + post: "/projects/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.read" + }; + } + + rpc ProjectByID(ProjectID) returns (ProjectView) { + option (google.api.http) = { + get: "/projects/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.read" + check_field_name: "Id" + }; + } + + rpc CreateProject(ProjectCreateRequest) returns (Project) { + option (google.api.http) = { + post: "/projects" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.create" + }; + } + + rpc UpdateProject(ProjectUpdateRequest) returns (Project) { + option (google.api.http) = { + put: "/projects/{id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.write" + check_field_name: "Id" + }; + } + + rpc DeactivateProject(ProjectID) returns (Project) { + option (google.api.http) = { + put: "/projects/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.write" + check_field_name: "Id" + }; + } + + rpc ReactivateProject(ProjectID) returns (Project) { + option (google.api.http) = { + put: "/projects/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.write" + check_field_name: "Id" + }; + } + + rpc RemoveProject(ProjectID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.delete" + check_field_name: "Id" + }; + } + + // returns all projects my organisation got granted from another organisation + rpc SearchGrantedProjects(GrantedProjectSearchRequest) returns (ProjectGrantSearchResponse) { + option (google.api.http) = { + post: "/grantedprojects/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.read" + }; + } + + // returns a project my organisation got granted from another organisation + rpc GetGrantedProjectByID(ProjectGrantID) returns (ProjectGrantView) { + option (google.api.http) = { + get: "/grantedprojects/{project_id}/grants/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.read" + check_field_name: "Id" + }; + } + + rpc GetProjectMemberRoles(google.protobuf.Empty) returns (ProjectMemberRoles) { + option (google.api.http) = { + get: "/projects/members/roles" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.member.read" + }; + } + + rpc SearchProjectMembers(ProjectMemberSearchRequest) returns (ProjectMemberSearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/members/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.member.read" + check_field_name: "ProjectId" + }; + } + + rpc AddProjectMember(ProjectMemberAdd) returns (ProjectMember) { + option (google.api.http) = { + post: "/projects/{id}/members" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.member.write" + check_field_name: "Id" + }; + } + + rpc ChangeProjectMember(ProjectMemberChange) returns (ProjectMember) { + option (google.api.http) = { + put: "/projects/{id}/members/{user_id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.member.write" + check_field_name: "Id" + }; + } + + rpc RemoveProjectMember(ProjectMemberRemove) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{id}/members/{user_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.member.delete" + check_field_name: "Id" + }; + } + + rpc SearchProjectRoles(ProjectRoleSearchRequest) returns (ProjectRoleSearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/roles/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.role.read" + check_field_name: "ProjectId" + }; + } + + rpc AddProjectRole(ProjectRoleAdd) returns (ProjectRole) { + option (google.api.http) = { + post: "/projects/{id}/roles" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.role.write" + check_field_name: "Id" + }; + } + + // add a list of project roles in one request + rpc BulkAddProjectRole(ProjectRoleAddBulk) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/projects/{id}/roles/_bulk" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.role.write" + check_field_name: "Id" + }; + } + + rpc ChangeProjectRole(ProjectRoleChange) returns (ProjectRole) { + option (google.api.http) = { + put: "/projects/{id}/roles/{key}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.role.write" + check_field_name: "Id" + }; + } + + // RemoveProjectRole removes role from UserGrants, ProjectGrants and from Project + rpc RemoveProjectRole(ProjectRoleRemove) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{id}/roles/{key}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.role.delete" + check_field_name: "Id" + }; + } + + rpc SearchApplications(ApplicationSearchRequest) returns (ApplicationSearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/applications/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + check_field_name: "ProjectId" + }; + } + + rpc ApplicationByID(ApplicationID) returns (ApplicationView) { + option (google.api.http) = { + get: "/projects/{project_id}/applications/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + check_field_name: "ProjectId" + }; + } + + // ApplicationChanges returns the event stream of the application object + rpc ApplicationChanges(ChangeRequest) returns (Changes) { + option (google.api.http) = { + get: "/projects/{id}/applications/{sec_id}/changes" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + }; + } + + rpc CreateOIDCApplication(OIDCApplicationCreate) returns (Application) { + option (google.api.http) = { + post: "/projects/{project_id}/applications/oidc" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc CreateAPIApplication(APIApplicationCreate) returns (Application) { + option (google.api.http) = { + post: "/projects/{project_id}/applications/api" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc UpdateApplication(ApplicationUpdate) returns (Application) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc DeactivateApplication(ApplicationID) returns (Application) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc ReactivateApplication(ApplicationID) returns (Application) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc RemoveApplication(ApplicationID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{project_id}/applications/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.delete" + check_field_name: "ProjectId" + }; + } + + rpc UpdateApplicationOIDCConfig(OIDCConfigUpdate) returns (OIDCConfig) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{application_id}/oidcconfig" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc RegenerateOIDCClientSecret(ApplicationID) returns (ClientSecret) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{id}/oidcconfig/_changeclientsecret" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc UpdateApplicationAPIConfig(APIConfigUpdate) returns (APIConfig) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{application_id}/apiconfig" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc RegenerateAPIClientSecret(ApplicationID) returns (ClientSecret) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{id}/apiconfig/_changeclientsecret" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc AddClientKey(AddClientKeyRequest) returns (AddClientKeyResponse){ + option (google.api.http) = { + post: "/projects/{project_id}/applications/{application_id}/keys" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc DeleteClientKey(ClientKeyIDRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{project_id}/applications/{application_id}/keys/{key_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc SearchClientKeys(ClientKeySearchRequest) returns (ClientKeySearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/applications/{application_id}/keys/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + check_field_name: "ProjectId" + }; + } + + rpc GetClientKey(ClientKeyIDRequest) returns (ClientKeyView) { + option (google.api.http) = { + get: "/projects/{project_id}/applications/{application_id}/keys/{key_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + check_field_name: "ProjectId" + }; + } + + rpc SearchProjectGrants(ProjectGrantSearchRequest) returns (ProjectGrantSearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/grants/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.read" + check_field_name: "ProjectId" + }; + } + + rpc ProjectGrantByID(ProjectGrantID) returns (ProjectGrantView) { + option (google.api.http) = { + get: "/projects/{project_id}/grants/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.read" + }; + } + + rpc CreateProjectGrant(ProjectGrantCreate) returns (ProjectGrant) { + option (google.api.http) = { + post: "/projects/{project_id}/grants" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.write" + }; + } + + rpc UpdateProjectGrant(ProjectGrantUpdate) returns (ProjectGrant) { + option (google.api.http) = { + put: "/projects/{project_id}/grants/{id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.write" + }; + } + + rpc DeactivateProjectGrant(ProjectGrantID) returns (ProjectGrant) { + option (google.api.http) = { + put: "/projects/{project_id}/grants/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.write" + }; + } + + rpc ReactivateProjectGrant(ProjectGrantID) returns (ProjectGrant) { + option (google.api.http) = { + put: "/projects/{project_id}/grants/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.write" + }; + } + + // RemoveProjectGrant removes project grant and all user grants for this project grant + rpc RemoveProjectGrant(ProjectGrantID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{project_id}/grants/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.delete" + }; + } + + rpc GetProjectGrantMemberRoles(google.protobuf.Empty) returns (ProjectGrantMemberRoles) { + option (google.api.http) = { + get: "/projects/grants/members/roles" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.member.read" + }; + } + + rpc SearchProjectGrantMembers(ProjectGrantMemberSearchRequest) returns (ProjectGrantMemberSearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/grants/{grant_id}/members/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.member.read" + }; + } + + rpc AddProjectGrantMember(ProjectGrantMemberAdd) returns (ProjectGrantMember) { + option (google.api.http) = { + post: "/projects/{project_id}/grants/{grant_id}/members" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.member.write" + }; + } + + rpc ChangeProjectGrantMember(ProjectGrantMemberChange) returns (ProjectGrantMember) { + option (google.api.http) = { + put: "/projects/{project_id}/grants/{grant_id}/members/{user_id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.member.write" + }; + } + + rpc RemoveProjectGrantMember(ProjectGrantMemberRemove) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{project_id}/grants/{grant_id}/members/{user_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.grant.member.delete" + }; + } + + rpc SearchUserGrants(UserGrantSearchRequest) returns (UserGrantSearchResponse) { + option (google.api.http) = { + post: "/users/grants/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.read" + }; + } + + rpc UserGrantByID(UserGrantID) returns (UserGrantView) { + option (google.api.http) = { + get: "/users/{user_id}/grants/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.read" + }; + } + + rpc CreateUserGrant(UserGrantCreate) returns (UserGrant) { + option (google.api.http) = { + post: "/users/{user_id}/grants" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.write" + }; + } + + rpc UpdateUserGrant(UserGrantUpdate) returns (UserGrant) { + option (google.api.http) = { + put: "/users/{user_id}/grants/{id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.write" + }; + } + + rpc DeactivateUserGrant(UserGrantID) returns (UserGrant) { + option (google.api.http) = { + put: "/users/{user_id}/grants/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.write" + }; + } + + rpc ReactivateUserGrant(UserGrantID) returns (UserGrant) { + option (google.api.http) = { + put: "/users/{user_id}/grants/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.write" + }; + } + + rpc RemoveUserGrant(UserGrantID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/grants/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.delete" + }; + } + + // remove a list of user grants in one request + rpc BulkRemoveUserGrant(UserGrantRemoveBulk) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/usersgrants/_bulk" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.grant.delete" + }; + } + + rpc IdpByID(IdpID) returns (IdpView) { + option (google.api.http) = { + get: "/orgs/me/idps/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.read" + }; + } + + rpc CreateOidcIdp(OidcIdpConfigCreate) returns (Idp) { + option (google.api.http) = { + post: "/orgs/me/idps/oidc" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + rpc UpdateIdpConfig(IdpUpdate) returns (Idp) { + option (google.api.http) = { + put: "/orgs/me/idps/{id}" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + rpc DeactivateIdpConfig(IdpID) returns (Idp) { + option (google.api.http) = { + put: "/orgs/me/idps/{id}/_deactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + rpc ReactivateIdpConfig(IdpID) returns (Idp) { + option (google.api.http) = { + put: "/orgs/me/idps/{id}/_reactivate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + rpc RemoveIdpConfig(IdpID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/idps/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + rpc UpdateOidcIdpConfig(OidcIdpConfigUpdate) returns (OidcIdpConfig) { + option (google.api.http) = { + put: "/orgs/me/idps/{idp_id}/oidcconfig" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + rpc SearchIdps(IdpSearchRequest) returns (IdpSearchResponse) { + option (google.api.http) = { + post: "/orgs/me/idps/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "org.idp.read" + }; + } + + rpc GetLoginPolicy(google.protobuf.Empty) returns (LoginPolicyView) { + option (google.api.http) = { + get: "/orgs/me/policies/login" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultLoginPolicy(google.protobuf.Empty) returns (LoginPolicyView) { + option (google.api.http) = { + get: "/orgs/default/policies/login" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreateLoginPolicy(LoginPolicyRequest) returns (LoginPolicy) { + option (google.api.http) = { + post: "/orgs/me/policies/login" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdateLoginPolicy(LoginPolicyRequest) returns (LoginPolicy) { + option (google.api.http) = { + put: "/orgs/me/policies/login" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveLoginPolicy(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/login" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + + rpc GetLoginPolicyIdpProviders(IdpProviderSearchRequest) returns (IdpProviderSearchResponse) { + option (google.api.http) = { + post: "/orgs/me/policies/login/idpproviders/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc AddIdpProviderToLoginPolicy(IdpProviderAdd) returns (IdpProvider) { + option (google.api.http) = { + post: "/orgs/me/policies/login/idpproviders" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveIdpProviderFromLoginPolicy(IdpProviderID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/login/idpproviders/{idp_config_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc GetLoginPolicySecondFactors(google.protobuf.Empty) returns (SecondFactorsResult) { + option (google.api.http) = { + get: "/orgs/me/policies/login/secondfactors/_search" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc AddSecondFactorToLoginPolicy(SecondFactor) returns (SecondFactor) { + option (google.api.http) = { + post: "/orgs/me/policies/login/secondfactors" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveSecondFactorFromLoginPolicy(SecondFactor) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/login/secondfactors/{second_factor}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc GetLoginPolicyMultiFactors(google.protobuf.Empty) returns (MultiFactorsResult) { + option (google.api.http) = { + get: "/orgs/me/policies/login/multifactors/_search" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc AddMultiFactorToLoginPolicy(MultiFactor) returns (MultiFactor) { + option (google.api.http) = { + post: "/orgs/me/policies/login/multifactors" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveMultiFactorFromLoginPolicy(MultiFactor) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/login/multifactors/{multi_factor}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc GetPasswordComplexityPolicy(google.protobuf.Empty) returns (PasswordComplexityPolicyView) { + option (google.api.http) = { + get: "/orgs/me/policies/password/complexity" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultPasswordComplexityPolicy(google.protobuf.Empty) returns (PasswordComplexityPolicyView) { + option (google.api.http) = { + get: "/orgs/default/policies/password/complexity" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreatePasswordComplexityPolicy(PasswordComplexityPolicyRequest) returns (PasswordComplexityPolicy) { + option (google.api.http) = { + post: "/orgs/me/policies/password/complexity" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdatePasswordComplexityPolicy(PasswordComplexityPolicyRequest) returns (PasswordComplexityPolicy) { + option (google.api.http) = { + put: "/orgs/me/policies/password/complexity" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemovePasswordComplexityPolicy(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/password/complexity" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + + rpc GetPasswordAgePolicy(google.protobuf.Empty) returns (PasswordAgePolicyView) { + option (google.api.http) = { + get: "/orgs/me/policies/password/age" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultPasswordAgePolicy(google.protobuf.Empty) returns (PasswordAgePolicyView) { + option (google.api.http) = { + get: "/orgs/default/policies/password/age" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreatePasswordAgePolicy(PasswordAgePolicyRequest) returns (PasswordAgePolicy) { + option (google.api.http) = { + post: "/orgs/me/policies/password/age" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdatePasswordAgePolicy(PasswordAgePolicyRequest) returns (PasswordAgePolicy) { + option (google.api.http) = { + put: "/orgs/me/policies/password/age" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemovePasswordAgePolicy(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/password/age" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + + rpc GetPasswordLockoutPolicy(google.protobuf.Empty) returns (PasswordLockoutPolicyView) { + option (google.api.http) = { + get: "/orgs/me/policies/password/lockout" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultPasswordLockoutPolicy(google.protobuf.Empty) returns (PasswordLockoutPolicyView) { + option (google.api.http) = { + get: "/orgs/default/policies/password/lockout" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreatePasswordLockoutPolicy(PasswordLockoutPolicyRequest) returns (PasswordLockoutPolicy) { + option (google.api.http) = { + post: "/orgs/me/policies/password/lockout" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdatePasswordLockoutPolicy(PasswordLockoutPolicyRequest) returns (PasswordLockoutPolicy) { + option (google.api.http) = { + put: "/orgs/me/policies/password/lockout" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemovePasswordLockoutPolicy(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/password/lockout" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + + rpc GetMailTemplate(google.protobuf.Empty) returns (MailTemplateView) { + option (google.api.http) = { + get: "/orgs/me/policies/mailtemplate" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultMailTemplate(google.protobuf.Empty) returns (MailTemplateView) { + option (google.api.http) = { + get: "/orgs/default/policies/mailtemplate" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreateMailTemplate(MailTemplateUpdate) returns (MailTemplate) { + option (google.api.http) = { + post: "/orgs/me/policies/mailtemplate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdateMailTemplate(MailTemplateUpdate) returns (MailTemplate) { + option (google.api.http) = { + put: "/orgs/me/policies/mailtemplate" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveMailTemplate(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/mailtemplate" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + + + rpc GetMailTexts(google.protobuf.Empty) returns (MailTextsView) { + option (google.api.http) = { + get: "/orgs/me/policies/mailtexts" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultMailTexts(google.protobuf.Empty) returns (MailTextsView) { + option (google.api.http) = { + get: "/orgs/default/policies/mailtexts" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreateMailText(MailTextUpdate) returns (MailText) { + option (google.api.http) = { + post: "/orgs/me/policies/mailtext" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdateMailText(MailTextUpdate) returns (MailText) { + option (google.api.http) = { + put: "/orgs/me/policies/mailtext" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveMailText(MailTextRemove) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/mailtext/type/{mail_text_type}/language/{language}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + + rpc GetLabelPolicy(google.protobuf.Empty) returns (LabelPolicyView) { + option (google.api.http) = { + get: "/orgs/me/policies/label" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc GetDefaultLabelPolicy(google.protobuf.Empty) returns (LabelPolicyView) { + option (google.api.http) = { + get: "/orgs/default/policies/label" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.read" + }; + } + + rpc CreateLabelPolicy(LabelPolicyRequest) returns (LabelPolicy) { + option (google.api.http) = { + post: "/orgs/me/policies/label" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc UpdateLabelPolicy(LabelPolicyRequest) returns (LabelPolicy) { + option (google.api.http) = { + put: "/orgs/me/policies/label" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.write" + }; + } + + rpc RemoveLabelPolicy(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/orgs/me/policies/label" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "policy.delete" + }; + } + +} + +message ZitadelDocs { + string issuer = 1; + string discovery_endpoint = 2; +} + +message Iam { + string global_org_id = 1; + string iam_project_id = 2; + IamSetupStep set_up_done = 3; + IamSetupStep set_up_started = 4; +} + +enum IamSetupStep { + iam_setup_step_UNDEFINED = 0; + iam_setup_step_1 = 1; + iam_setup_step_2 = 2; +} + +message ChangeRequest { + string id = 1; + string sec_id = 2; + uint64 limit = 3; + uint64 sequence_offset = 4; + bool asc = 5; +} + +message Changes { + repeated Change changes = 1; + uint64 offset = 2; + uint64 limit = 3; +} + +message Change { + google.protobuf.Timestamp change_date = 1; + caos.zitadel.api.v1.LocalizedMessage event_type = 2; + uint64 sequence = 3; + string editor_id = 4; + string editor = 5; + google.protobuf.Struct data = 6; +} + +message ApplicationID { + string id = 1 [(validate.rules).string.min_len = 1]; + string project_id = 2 [(validate.rules).string.min_len = 1]; +} + +message ProjectID { + string id = 1 [(validate.rules).string.min_len = 1]; +} + +message UserID { + string id = 1 [(validate.rules).string.min_len = 1]; +} + +message WebAuthNTokens { + repeated WebAuthNToken tokens = 1; +} + +message WebAuthNToken { + string id = 1; + string name = 2; + MFAState state = 3; +} + +message WebAuthNTokenID { + string user_id = 1 [(validate.rules).string.min_len = 1]; + string id = 2 [(validate.rules).string.min_len = 1]; +} + +message LoginName { + string login_name = 1 [(validate.rules).string.min_len = 1]; +} + +message UniqueUserRequest { + string user_name = 1 [(validate.rules).string.pattern = "^[^[:space:]]{1,200}$"]; + string email = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message UniqueUserResponse { + bool is_unique = 1; +} + +message CreateUserRequest { + string user_name = 1 [(validate.rules).string.pattern = "^[^[:space:]]{1,200}$"]; + + oneof user { + option (validate.required) = true; + + CreateHumanRequest human = 2; + CreateMachineRequest machine = 3; + } +} + +message ImportHumanRequest { + string user_name = 1 [(validate.rules).string.pattern = "^[^[:space:]]{1,200}$"]; + string first_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string last_name = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string nick_name = 4 [(validate.rules).string = {max_len: 200}]; + string preferred_language = 5 [(validate.rules).string = {max_len: 200}]; + Gender gender = 6; + string email = 7 [(validate.rules).string = {min_len: 1, max_len: 200, email: true}]; + bool is_email_verified = 8; + string phone = 9 [(validate.rules).string = {max_len: 20}]; + bool is_phone_verified = 10; + string country = 11 [(validate.rules).string = {max_len: 200}]; + string locality = 12 [(validate.rules).string = {max_len: 200}]; + string postal_code = 13 [(validate.rules).string = {max_len: 200}]; + string region = 14 [(validate.rules).string = {max_len: 200}]; + string street_address = 15 [(validate.rules).string = {max_len: 200}]; + string password = 16 [(validate.rules).string = {max_len: 72}]; + bool password_change_required = 17; +} + +message CreateHumanRequest { + string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string nick_name = 3 [(validate.rules).string = {max_len: 200}]; + string preferred_language = 4 [(validate.rules).string = {max_len: 200}]; + Gender gender = 5; + string email = 6 [(validate.rules).string = {min_len: 1, max_len: 200, email: true}]; + bool is_email_verified = 7; + string phone = 8 [(validate.rules).string = {max_len: 20}]; + bool is_phone_verified = 9; + string country = 10 [(validate.rules).string = {max_len: 200}]; + string locality = 11 [(validate.rules).string = {max_len: 200}]; + string postal_code = 12 [(validate.rules).string = {max_len: 200}]; + string region = 13 [(validate.rules).string = {max_len: 200}]; + string street_address = 14 [(validate.rules).string = {max_len: 200}]; + string password = 15 [(validate.rules).string = {max_len: 72}]; +} + +message CreateMachineRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string description = 2 [(validate.rules).string.max_len = 500]; +} + +message UserResponse { + string id = 1; + UserState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + uint64 sequence = 5; + string user_name = 6; + + oneof user { + option (validate.required) = true; + + HumanResponse human = 7; + MachineResponse machine = 8; + } +} + +enum UserState { + USERSTATE_UNSPECIFIED = 0; + USERSTATE_ACTIVE = 1; + USERSTATE_INACTIVE = 2; + USERSTATE_DELETED = 3; + USERSTATE_LOCKED = 4; + USERSTATE_SUSPEND = 5; + USERSTATE_INITIAL = 6; +} + +enum Gender { + GENDER_UNSPECIFIED = 0; + GENDER_FEMALE = 1; + GENDER_MALE = 2; + GENDER_DIVERSE = 3; +} + +message UserView { + string id = 1; + UserState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + uint64 sequence = 5; + repeated string login_names = 6; + string preferred_login_name = 7; + google.protobuf.Timestamp last_login = 8; + string resource_owner = 9; + string user_name = 10; + + oneof user { + option (validate.required) = true; + + HumanView human = 11; + MachineView machine = 12; + } +} + +message HumanResponse { + string first_name = 1; + string last_name = 2; + string display_name = 3; + string nick_name = 4; + string preferred_language = 5; + Gender gender = 6; + string email = 7; + bool is_email_verified = 8; + string phone = 9; + bool is_phone_verified = 10; + string country = 11; + string locality = 12; + string postal_code = 13; + string region = 14; + string street_address = 15; +} + +message HumanView { + google.protobuf.Timestamp password_changed = 1; + string first_name = 2; + string last_name = 3; + string display_name = 4; + string nick_name = 5; + string preferred_language = 6; + Gender gender = 7; + string email = 8; + bool is_email_verified = 9; + string phone = 10; + bool is_phone_verified = 11; + string country = 12; + string locality = 13; + string postal_code = 14; + string region = 15; + string street_address = 16; +} + +message MachineResponse { + string name = 1; + string description = 2; +} + +message MachineView { + google.protobuf.Timestamp last_key_added = 1; + string name = 2; + string description = 3; +} + +message UpdateMachineRequest { + string id = 1 [(validate.rules).string.min_len = 1]; + string description = 2 [(validate.rules).string.max_len = 500]; +} + +message AddMachineKeyRequest { + string user_id = 1 [(validate.rules).string.min_len = 1]; + MachineKeyType type = 2 [(validate.rules).enum = {not_in: [0]}]; + google.protobuf.Timestamp expiration_date = 3; +} + +message AddMachineKeyResponse { + string id = 1; + google.protobuf.Timestamp creation_date = 2; + uint64 sequence = 3; + + MachineKeyType type = 4; + google.protobuf.Timestamp expiration_date = 5; + bytes key_details = 6; +} + +message MachineKeyIDRequest { + string user_id = 1 [(validate.rules).string.min_len = 1]; + string key_id = 2 [(validate.rules).string.min_len = 1]; +} + +message MachineKeyView { + string id = 1; + MachineKeyType type = 2; + uint64 sequence = 3; + + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp expiration_date = 5; +} + +enum MachineKeyType { + MACHINEKEY_UNSPECIFIED = 0; + MACHINEKEY_JSON = 1; +} + +message MachineKeySearchRequest { + uint64 offset = 1; + uint64 limit = 2; + bool asc = 3; + string user_id = 4 [(validate.rules).string.min_len = 1]; +} + +message MachineKeySearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated MachineKeyView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message UserSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + UserSearchKey sorting_column = 3; + bool asc = 4; + repeated UserSearchQuery queries = 5; +} + +message UserSearchQuery { + UserSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum UserSearchKey { + USERSEARCHKEY_UNSPECIFIED = 0; + USERSEARCHKEY_USER_NAME = 1; + USERSEARCHKEY_FIRST_NAME = 2; + USERSEARCHKEY_LAST_NAME = 3; + USERSEARCHKEY_NICK_NAME = 4; + USERSEARCHKEY_DISPLAY_NAME = 5; + USERSEARCHKEY_EMAIL = 6; + USERSEARCHKEY_STATE = 7; + USERSEARCHKEY_TYPE = 8; +} + +message UserSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated UserView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +enum SearchMethod { + SEARCHMETHOD_EQUALS = 0; + SEARCHMETHOD_STARTS_WITH = 1; + SEARCHMETHOD_CONTAINS = 2; + SEARCHMETHOD_EQUALS_IGNORE_CASE = 3; + SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4; + SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5; + SEARCHMETHOD_NOT_EQUALS = 6; + SEARCHMETHOD_GREATER_THAN = 7; + SEARCHMETHOD_LESS_THAN = 8; + SEARCHMETHOD_IS_ONE_OF = 9; + SEARCHMETHOD_LIST_CONTAINS = 10; +} + +message UserProfile { + string id = 1; + string first_name = 2; + string last_name = 3; + string nick_name = 4; + string display_name = 5; + string preferred_language = 6; + Gender gender = 7; + uint64 sequence = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; +} + +message UserProfileView { + string id = 1; + string first_name = 2; + string last_name = 3; + string nick_name = 4; + string display_name = 5; + string preferred_language = 6; + Gender gender = 7; + uint64 sequence = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; + repeated string login_names = 11; + string preferred_login_name = 12; +} + +message UpdateUserProfileRequest { + string id = 1; + string first_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string last_name = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string nick_name = 4 [(validate.rules).string = {max_len: 200}]; + string preferred_language = 5 [(validate.rules).string = {max_len: 200}]; + Gender gender = 6; +} + +message UpdateUserUserNameRequest { + string id = 1; + string user_name = 2 [(validate.rules).string.pattern = "^[^[:space:]]{1,200}$"]; +} + +message UserEmail { + string id = 1; + string email = 2; + bool is_email_verified = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message UserEmailView { + string id = 1; + string email = 2; + bool is_email_verified = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message UpdateUserEmailRequest { + string id = 1; + string email = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + bool is_email_verified = 3; +} + +message UserPhone { + string id = 1; + string phone = 2; + bool is_phone_verified = 3; + uint64 sequence = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; +} + +message UserPhoneView { + string id = 1; + string phone = 2; + bool is_phone_verified = 3; + uint64 sequence = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; +} + +message UpdateUserPhoneRequest { + string id = 1 [(validate.rules).string.min_len = 1]; + string phone = 2 [(validate.rules).string = {min_len: 1, max_len: 20}]; + bool is_phone_verified = 3; +} + +message UserAddress { + string id = 1; + string country = 2; + string locality = 3; + string postal_code = 4; + string region = 5; + string street_address = 6; + uint64 sequence = 7; + google.protobuf.Timestamp creation_date = 8; + google.protobuf.Timestamp change_date = 9; +} + +message UserAddressView { + string id = 1; + string country = 2; + string locality = 3; + string postal_code = 4; + string region = 5; + string street_address = 6; + uint64 sequence = 7; + google.protobuf.Timestamp creation_date = 8; + google.protobuf.Timestamp change_date = 9; +} + +message UpdateUserAddressRequest { + string id = 1 [(validate.rules).string.min_len = 1]; + string country = 2 [(validate.rules).string = {max_len: 200}]; + string locality = 3 [(validate.rules).string = {max_len: 200}]; + string postal_code = 4 [(validate.rules).string = {max_len: 200}]; + string region = 5 [(validate.rules).string = {max_len: 200}]; + string street_address = 6 [(validate.rules).string = {max_len: 200}]; +} + +message UserMultiFactors { + repeated UserMultiFactor mfas = 1; +} + +message UserMultiFactor { + MfaType type = 1; + MFAState state = 2; + string attribute = 3; + string id = 4; +} + +enum MfaType { + MFATYPE_UNSPECIFIED = 0; + MFATYPE_OTP = 1; + MFATYPE_U2F = 2; +} + +enum MFAState { + MFASTATE_UNSPECIFIED = 0; + MFASTATE_NOT_READY = 1; + MFASTATE_READY = 2; + MFASTATE_REMOVED = 3; +} + +message PasswordRequest { + string id = 1 [(validate.rules).string.min_len = 1]; + string password = 2 [(validate.rules).string = {min_len: 1, max_len: 72}]; +} + +message SetPasswordNotificationRequest { + string id = 1 [(validate.rules).string.min_len = 1]; + NotificationType type = 2; +} + +enum NotificationType { + NOTIFICATIONTYPE_EMAIL = 0; + NOTIFICATIONTYPE_SMS = 1; +} + +message InitialMailRequest { + string id = 1 [(validate.rules).string.min_len = 1]; + string email = 2; +} + +enum PolicyState { + POLICYSTATE_UNSPECIFIED = 0; + POLICYSTATE_ACTIVE = 1; + POLICYSTATE_INACTIVE = 2; + POLICYSTATE_DELETED = 3; +} + +message OrgIamPolicyView { + bool user_login_must_be_domain = 1; + bool default = 2; +} + +message OrgCreateRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message Org { + string id = 1; + OrgState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + uint64 sequence = 6; +} + +message OrgView { + string id = 1; + OrgState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + uint64 sequence = 6; +} + +enum OrgState { + ORGSTATE_UNSPECIFIED = 0; + ORGSTATE_ACTIVE = 1; + ORGSTATE_INACTIVE = 2; +} + +message Domain { + string domain = 1 [(validate.rules).string = {min_len: 1}]; +} + +message OrgDomain { + string org_id = 1; + google.protobuf.Timestamp creation_date = 2; + google.protobuf.Timestamp change_date = 3; + string domain = 4; + bool verified = 5; + bool primary = 6; + uint64 sequence = 7; +} + +message OrgDomainView { + string org_id = 1; + google.protobuf.Timestamp creation_date = 2; + google.protobuf.Timestamp change_date = 3; + string domain = 4; + bool verified = 5; + bool primary = 6; + uint64 sequence = 7; + OrgDomainValidationType validation_type = 8; +} + +message AddOrgDomainRequest { + string domain = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message OrgDomainValidationRequest { + string domain = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + OrgDomainValidationType type = 2 [(validate.rules).enum = {not_in: [0]}]; +} + +enum OrgDomainValidationType { + ORGDOMAINVALIDATIONTYPE_UNSPECIFIED = 0; + ORGDOMAINVALIDATIONTYPE_HTTP = 1; + ORGDOMAINVALIDATIONTYPE_DNS = 2; +} + +message OrgDomainValidationResponse { + string token = 1; + string url = 2; +} + +message ValidateOrgDomainRequest { + string domain = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message PrimaryOrgDomainRequest { + string domain = 1 [(validate.rules).string = {min_len: 1}]; +} + +message RemoveOrgDomainRequest { + string domain = 1 [(validate.rules).string = {min_len: 1}]; +} + +message OrgDomainSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated OrgDomainView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message OrgDomainSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated OrgDomainSearchQuery queries = 3; +} + +message OrgDomainSearchQuery { + OrgDomainSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum OrgDomainSearchKey { + ORGDOMAINSEARCHKEY_UNSPECIFIED = 0; + ORGDOMAINSEARCHKEY_DOMAIN = 1; +} + +message OrgMemberRoles { + repeated string roles = 1; +} + +message OrgMember { + string user_id = 1; + repeated string roles = 2; + google.protobuf.Timestamp change_date = 3; + google.protobuf.Timestamp creation_date = 4; + uint64 sequence = 5; +} + +message AddOrgMemberRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 2; +} + +message ChangeOrgMemberRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 2; +} + +message RemoveOrgMemberRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message OrgMemberSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated OrgMemberView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message OrgMemberView { + string user_id = 1; + repeated string roles = 2; + google.protobuf.Timestamp change_date = 3; + google.protobuf.Timestamp creation_date = 4; + uint64 sequence = 5; + string user_name = 6; + string email = 7; + string first_name = 8; + string last_name = 9; + string display_name = 10; +} + +message OrgMemberSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated OrgMemberSearchQuery queries = 3; +} + +message OrgMemberSearchQuery { + OrgMemberSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum OrgMemberSearchKey { + ORGMEMBERSEARCHKEY_UNSPECIFIED = 0; + ORGMEMBERSEARCHKEY_FIRST_NAME = 1; + ORGMEMBERSEARCHKEY_LAST_NAME = 2; + ORGMEMBERSEARCHKEY_EMAIL = 3; + ORGMEMBERSEARCHKEY_USER_ID = 4; +} + +message ProjectCreateRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + bool project_role_assertion = 2; + bool project_role_check = 3; +} + +message ProjectUpdateRequest { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + bool project_role_assertion = 3; + bool project_role_check = 4; +} + +message ProjectSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ProjectView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ProjectView { + string project_id = 1; + string name = 2; + ProjectState state = 3; + google.protobuf.Timestamp change_date = 4; + google.protobuf.Timestamp creation_date = 5; + string resource_owner = 6; + uint64 sequence = 7; + bool project_role_assertion = 8; + bool project_role_check = 9; +} + +message ProjectSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated ProjectSearchQuery queries = 3; +} + +message ProjectSearchQuery { + ProjectSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum ProjectSearchKey { + PROJECTSEARCHKEY_UNSPECIFIED = 0; + PROJECTSEARCHKEY_PROJECT_NAME = 1; +} + +message Projects { + repeated Project projects = 1; +} + +message Project { + string id = 1; + string name = 2; + ProjectState state = 3; + google.protobuf.Timestamp change_date = 4; + google.protobuf.Timestamp creation_date = 5; + uint64 sequence = 6; + bool project_role_assertion = 7; + bool project_role_check = 8; +} + +enum ProjectState { + PROJECTSTATE_UNSPECIFIED = 0; + PROJECTSTATE_ACTIVE = 1; + PROJECTSTATE_INACTIVE = 2; +} + +message ProjectMemberRoles { + repeated string roles = 1; +} + +message ProjectMember { + string user_id = 1; + repeated string roles = 2; + google.protobuf.Timestamp change_date = 3; + google.protobuf.Timestamp creation_date = 4; + uint64 sequence = 5; +} + +message ProjectMemberAdd { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string user_id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 3; +} + +message ProjectMemberChange { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string user_id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 3; +} + +message ProjectMemberRemove { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string user_id = 2 [(validate.rules).string = {min_len: 1}]; +} + +message ProjectRoleAdd { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string key = 2; + string display_name = 3; + string group = 4; +} + +message ProjectRoleAddBulk { + string id = 1 [(validate.rules).string = {min_len: 1}]; + repeated ProjectRoleAdd project_roles = 2; +} + +message ProjectRoleChange { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string key = 2 [(validate.rules).string = {min_len: 1}]; + string display_name = 3; + string group = 4; +} + +message ProjectRole { + string project_id = 1; + string key = 2; + string display_name = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; + string group = 6; + uint64 sequence = 7; +} + +message ProjectRoleView { + string project_id = 1; + string key = 2; + string display_name = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; + string group = 6; + uint64 sequence = 7; +} + +message ProjectRoleRemove { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string key = 2 [(validate.rules).string = {min_len: 1}]; +} + +message ProjectRoleSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ProjectRoleView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ProjectRoleSearchRequest { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + uint64 offset = 2; + uint64 limit = 3; + repeated ProjectRoleSearchQuery queries = 4; +} + +message ProjectRoleSearchQuery { + ProjectRoleSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum ProjectRoleSearchKey { + PROJECTROLESEARCHKEY_UNSPECIFIED = 0; + PROJECTROLESEARCHKEY_KEY = 1; + PROJECTROLESEARCHKEY_DISPLAY_NAME = 2; +} + +message ProjectMemberView { + string user_id = 1; + string user_name = 2; + string email = 3; + string first_name = 4; + string last_name = 5; + repeated string roles = 6; + google.protobuf.Timestamp change_date = 7; + google.protobuf.Timestamp creation_date = 8; + uint64 sequence = 10; + string display_name = 11; +} + +message ProjectMemberSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ProjectMemberView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ProjectMemberSearchRequest { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + uint64 offset = 2; + uint64 limit = 3; + repeated ProjectMemberSearchQuery queries = 4; +} + +message ProjectMemberSearchQuery { + ProjectMemberSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum ProjectMemberSearchKey { + PROJECTMEMBERSEARCHKEY_UNSPECIFIED = 0; + PROJECTMEMBERSEARCHKEY_FIRST_NAME = 1; + PROJECTMEMBERSEARCHKEY_LAST_NAME = 2; + PROJECTMEMBERSEARCHKEY_EMAIL = 3; + PROJECTMEMBERSEARCHKEY_USER_ID = 4; + PROJECTMEMBERSEARCHKEY_USER_NAME = 5; +} + +enum AppState { + APPSTATE_UNSPECIFIED = 0; + APPSTATE_ACTIVE = 1; + APPSTATE_INACTIVE = 2; +} + +message Application { + string id = 1; + AppState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + oneof app_config { + OIDCConfig oidc_config = 8; + APIConfig api_config = 10; + } + uint64 sequence = 9; +} + +message ApplicationUpdate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string id = 2 [(validate.rules).string = {min_len: 1}]; + string name = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message OIDCConfig { + repeated string redirect_uris = 1; + repeated OIDCResponseType response_types = 2; + repeated OIDCGrantType grant_types = 3; + OIDCApplicationType application_type = 4; + string client_id = 5; + string client_secret = 6; + OIDCAuthMethodType auth_method_type = 7; + repeated string post_logout_redirect_uris = 8; + OIDCVersion version = 9; + bool none_compliant = 10; + repeated caos.zitadel.api.v1.LocalizedMessage compliance_problems = 11; + bool dev_mode = 12; + OIDCTokenType access_token_type = 13; + bool access_token_role_assertion = 14; + bool id_token_role_assertion = 15; + bool id_token_userinfo_assertion = 16; + google.protobuf.Duration clock_skew = 17; +} + +message OIDCApplicationCreate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string redirect_uris = 3; + repeated OIDCResponseType response_types = 4; + repeated OIDCGrantType grant_types = 5; + OIDCApplicationType application_type = 6; + OIDCAuthMethodType auth_method_type = 7; + repeated string post_logout_redirect_uris = 8; + OIDCVersion version = 9; + bool dev_mode = 10; + OIDCTokenType access_token_type = 11; + bool access_token_role_assertion = 12; + bool id_token_role_assertion = 13; + bool id_token_userinfo_assertion = 14; + google.protobuf.Duration clock_skew = 15 [(validate.rules).duration = {gte: {}, lte: {seconds: 5}}]; +} + +message APIApplicationCreate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + APIAuthMethodType auth_method_type = 3; +} + +message APIConfig { + string client_id = 1; + string client_secret = 2; + APIAuthMethodType auth_method_type = 3; +} + +enum OIDCVersion { + OIDCV1_0 = 0; +} + +enum OIDCTokenType { + OIDCTokenType_Bearer = 0; + OIDCTokenType_JWT = 1; +} + +message OIDCConfigUpdate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string application_id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string redirect_uris = 3; + repeated OIDCResponseType response_types = 4; + repeated OIDCGrantType grant_types = 5; + OIDCApplicationType application_type = 6; + OIDCAuthMethodType auth_method_type = 7; + repeated string post_logout_redirect_uris = 8; + bool dev_mode = 9; + OIDCTokenType access_token_type = 10; + bool access_token_role_assertion = 11; + bool id_token_role_assertion = 12; + bool id_token_userinfo_assertion = 13; + google.protobuf.Duration clock_skew = 14 [(validate.rules).duration = {gte: {}, lte: {seconds: 5}}]; +} + +message APIConfigUpdate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string application_id = 2 [(validate.rules).string = {min_len: 1}]; + APIAuthMethodType auth_method_type = 7; +} + +enum OIDCResponseType { + OIDCRESPONSETYPE_CODE = 0; + OIDCRESPONSETYPE_ID_TOKEN = 1; + OIDCRESPONSETYPE_ID_TOKEN_TOKEN = 2; +} + +enum OIDCGrantType { + OIDCGRANTTYPE_AUTHORIZATION_CODE = 0; + OIDCGRANTTYPE_IMPLICIT = 1; + OIDCGRANTTYPE_REFRESH_TOKEN = 2; +} + +enum OIDCApplicationType { + OIDCAPPLICATIONTYPE_WEB = 0; + OIDCAPPLICATIONTYPE_USER_AGENT = 1; + OIDCAPPLICATIONTYPE_NATIVE = 2; +} + +enum OIDCAuthMethodType { + OIDCAUTHMETHODTYPE_BASIC = 0; + OIDCAUTHMETHODTYPE_POST = 1; + OIDCAUTHMETHODTYPE_NONE = 2; + OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT = 3; +} + +enum APIAuthMethodType { + APIAUTHMETHODTYPE_BASIC = 0; + APIAUTHMETHODTYPE_PRIVATE_KEY_JWT = 1; +} + +message ClientSecret { + string client_secret = 1; +} + +message ApplicationView { + string id = 1; + AppState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + oneof app_config { + OIDCConfig oidc_config = 8; + APIConfig api_config = 10; + } + + uint64 sequence = 9; +} + +message ApplicationSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ApplicationView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ApplicationSearchRequest { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + uint64 offset = 2; + uint64 limit = 3; + repeated ApplicationSearchQuery queries = 4; +} + +message ApplicationSearchQuery { + ApplicationSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum ApplicationSearchKey { + APPLICATIONSERACHKEY_UNSPECIFIED = 0; + APPLICATIONSEARCHKEY_APP_NAME = 1; +} + +message AddClientKeyRequest { + string project_id = 1 [(validate.rules).string.min_len = 1]; + string application_id = 2 [(validate.rules).string.min_len = 1]; + AuthNKeyType type = 3 [(validate.rules).enum = {not_in: [0]}]; + google.protobuf.Timestamp expiration_date = 4; +} + +message AddClientKeyResponse { + string id = 1; + google.protobuf.Timestamp creation_date = 2; + uint64 sequence = 3; + + AuthNKeyType type = 4; + google.protobuf.Timestamp expiration_date = 5; + bytes key_details = 6; +} + +message ClientKeyIDRequest { + string project_id = 1 [(validate.rules).string.min_len = 1]; + string application_id = 2 [(validate.rules).string.min_len = 1]; + string key_id = 3 [(validate.rules).string.min_len = 1]; +} + +message ClientKeyView { + string id = 1; + AuthNKeyType type = 2; + uint64 sequence = 3; + + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp expiration_date = 5; +} + +enum AuthNKeyType { + AUTHNKEY_UNSPECIFIED = 0; + AUTHNKEY_JSON = 1; +} + +message ClientKeySearchRequest { + uint64 offset = 1; + uint64 limit = 2; + bool asc = 3; + string project_id = 4 [(validate.rules).string.min_len = 1]; + string application_id = 5 [(validate.rules).string.min_len = 1]; +} + +message ClientKeySearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ClientKeyView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ProjectGrant { + string id = 1; + string project_id = 2; + string granted_org_id = 3; + repeated string role_keys = 4; + ProjectGrantState state = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; + uint64 sequence = 9; +} + +message ProjectGrantCreate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string granted_org_id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string role_keys = 3; +} + +message ProjectGrantUpdate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string role_keys = 3; +} + +message ProjectGrantID { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string id = 2 [(validate.rules).string = {min_len: 1}]; +} + +enum ProjectGrantState { + PROJECTGRANTSTATE_UNSPECIFIED = 0; + PROJECTGRANTSTATE_ACTIVE = 1; + PROJECTGRANTSTATE_INACTIVE = 2; +} + +message ProjectGrantView { + string id = 1; + string project_id = 2; + string granted_org_id = 3; + string granted_org_name = 4; + repeated string role_keys = 5; + ProjectGrantState state = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; + string project_name = 9; + uint64 sequence = 10; + string resource_owner = 11; + string resource_owner_name = 12; +} + +message ProjectGrantSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ProjectGrantView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message GrantedProjectSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated ProjectSearchQuery queries = 3; +} + +message ProjectGrantSearchRequest { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + uint64 offset = 2; + uint64 limit = 3; + repeated ProjectGrantSearchQuery queries = 4; +} + +message ProjectGrantSearchQuery { + ProjectGrantSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum ProjectGrantSearchKey { + PROJECTGRANTSEARCHKEY_UNSPECIFIED = 0; + PROJECTGRANTSEARCHKEY_PROJECT_NAME = 1; + PROJECTGRANTSEARCHKEY_ROLE_KEY = 2; +} + +message ProjectGrantMemberRoles { + repeated string roles = 1; +} + +message ProjectGrantMember { + string user_id = 1; + repeated string roles = 2; + google.protobuf.Timestamp change_date = 3; + google.protobuf.Timestamp creation_date = 4; + uint64 sequence = 5; +} + +message ProjectGrantMemberAdd { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string grant_id = 2 [(validate.rules).string = {min_len: 1}]; + string user_id = 3 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 4; +} + +message ProjectGrantMemberChange { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string grant_id = 2 [(validate.rules).string = {min_len: 1}]; + string user_id = 3 [(validate.rules).string = {min_len: 1}]; + repeated string roles = 4; +} + +message ProjectGrantMemberRemove { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string grant_id = 2 [(validate.rules).string = {min_len: 1}]; + string user_id = 3 [(validate.rules).string = {min_len: 1}]; +} + +message ProjectGrantMemberView { + string user_id = 1; + string user_name = 2; + string email = 3; + string first_name = 4; + string last_name = 5; + repeated string roles = 6; + google.protobuf.Timestamp change_date = 7; + google.protobuf.Timestamp creation_date = 8; + uint64 sequence = 9; + string display_name = 10; +} + +message ProjectGrantMemberSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ProjectGrantMemberView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ProjectGrantMemberSearchRequest { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string grant_id = 2 [(validate.rules).string = {min_len: 1}]; + uint64 offset = 3; + uint64 limit = 4; + repeated ProjectGrantMemberSearchQuery queries = 5; +} + +message ProjectGrantMemberSearchQuery { + ProjectGrantMemberSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum ProjectGrantMemberSearchKey { + PROJECTGRANTMEMBERSEARCHKEY_UNSPECIFIED = 0; + PROJECTGRANTMEMBERSEARCHKEY_FIRST_NAME = 1; + PROJECTGRANTMEMBERSEARCHKEY_LAST_NAME = 2; + PROJECTGRANTMEMBERSEARCHKEY_EMAIL = 3; + PROJECTGRANTMEMBERSEARCHKEY_USER_ID = 4; + PROJECTGRANTMEMBERSEARCHKEY_USER_NAME = 5; +} + +message UserGrant { + string id = 1; + string user_id = 2; + string org_id = 3; + string project_id = 4; + repeated string role_keys = 5; + UserGrantState state = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; + uint64 sequence = 9; + string grant_id = 10; +} + +message UserGrantCreate { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + string project_id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string role_keys = 3; + string grant_id = 4; +} + +message UserGrantUpdate { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + string id = 2 [(validate.rules).string = {min_len: 1}]; + repeated string role_keys = 3; +} + +message UserGrantRemoveBulk { + repeated string ids = 1 [(validate.rules).repeated.min_items = 1]; +} + +message UserGrantID { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + string id = 2 [(validate.rules).string = {min_len: 1}]; +} + +enum UserGrantState { + USERGRANTSTATE_UNSPECIFIED = 0; + USERGRANTSTATE_ACTIVE = 1; + USERGRANTSTATE_INACTIVE = 2; +} + +message UserGrantView { + string id = 1; + string user_id = 2; + string org_id = 3; + string project_id = 4; + repeated string role_keys = 5; + UserGrantState state = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; + string user_name = 9; + string first_name = 10; + string last_name = 11; + string email = 12; + string org_name = 13; + string org_domain = 14; + string project_name = 15; + uint64 sequence = 16; + string resource_owner = 17; + string display_name = 18; + string grant_id = 19; +} + + +message UserGrantSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated UserGrantView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message UserGrantSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated UserGrantSearchQuery queries = 3; +} + +message UserGrantSearchQuery { + UserGrantSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2 [(validate.rules).enum.defined_only = true]; + string value = 3; +} + +enum UserGrantSearchKey { + USERGRANTSEARCHKEY_UNSPECIFIED = 0; + USERGRANTSEARCHKEY_PROJECT_ID = 1; + USERGRANTSEARCHKEY_USER_ID = 2; + USERGRANTSEARCHKEY_WITH_GRANTED = 3; + USERGRANTSEARCHKEY_ROLE_KEY = 4; + USERGRANTSEARCHKEY_GRANT_ID = 5; + USERGRANTSEARCHKEY_USER_NAME = 6; + USERGRANTSEARCHKEY_FIRST_NAME = 7; + USERGRANTSEARCHKEY_LAST_NAME = 8; + USERGRANTSEARCHKEY_EMAIL = 9; + USERGRANTSEARCHKEY_ORG_NAME = 10; + USERGRANTSEARCHKEY_ORG_DOMAIN = 11; + USERGRANTSEARCHKEY_PROJECT_NAME = 12; + USERGRANTSEARCHKEY_DISPLAY_NAME = 13; +} + +message UserMembershipSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated UserMembershipView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message UserMembershipSearchRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1}]; + uint64 offset = 2; + uint64 limit = 3; + repeated UserMembershipSearchQuery queries = 4; +} + +message UserMembershipSearchQuery { + UserMembershipSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2 [(validate.rules).enum = {in: [0]}]; + string value = 3; +} + +enum UserMembershipSearchKey { + USERMEMBERSHIPSEARCHKEY_UNSPECIFIED = 0; + USERMEMBERSHIPSEARCHKEY_TYPE = 1; + USERMEMBERSHIPSEARCHKEY_OBJECT_ID = 2; +} + +message UserMembershipView { + string user_id = 1; + MemberType member_type = 2; + string aggregate_id = 3; + string object_id = 4; + repeated string roles = 5; + string display_name = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; + uint64 sequence = 9; + string resource_owner = 10; +} + +enum MemberType { + MEMBERTYPE_UNSPECIFIED = 0; + MEMBERTYPE_ORGANISATION = 1; + MEMBERTYPE_PROJECT = 2; + MEMBERTYPE_PROJECT_GRANT = 3; +} + +message IdpID { + string id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message Idp { + string id = 1; + IdpState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + IdpStylingType styling_type = 6; + oneof idp_config { + OidcIdpConfig oidc_config = 7; + } + uint64 sequence = 8; +} + +message IdpUpdate { + string id = 1 [(validate.rules).string = {min_len: 1}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + IdpStylingType styling_type = 3; +} + +message OidcIdpConfig { + string client_id = 1; + string client_secret = 2; + string issuer = 3; + repeated string scopes = 4; + OIDCMappingField idp_display_name_mapping = 5; + OIDCMappingField username_mapping = 6; +} + +enum IdpStylingType { + IDPSTYLINGTYPE_UNSPECIFIED = 0; + IDPSTYLINGTYPE_GOOGLE = 1; +} + +enum IdpState { + IDPCONFIGSTATE_UNSPECIFIED = 0; + IDPCONFIGSTATE_ACTIVE = 1; + IDPCONFIGSTATE_INACTIVE = 2; +} + +enum OIDCMappingField { + OIDCMAPPINGFIELD_UNSPECIFIED = 0; + OIDCMAPPINGFIELD_PREFERRED_USERNAME = 1; + OIDCMAPPINGFIELD_EMAIL = 2; +} + +message OidcIdpConfigCreate { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + IdpStylingType styling_type = 2; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 6; + OIDCMappingField idp_display_name_mapping = 7; + OIDCMappingField username_mapping = 8; +} + +message OidcIdpConfigUpdate { + string idp_id = 1 [(validate.rules).string = {min_len: 1}]; + string client_id = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 3; + string issuer = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 5; + OIDCMappingField idp_display_name_mapping = 6; + OIDCMappingField username_mapping = 7; +} + +message IdpSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated IdpView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message IdpView { + string id = 1; + IdpState state = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + string name = 5; + IdpStylingType styling_type = 6; + IdpProviderType provider_type = 7; + oneof idp_config_view { + OidcIdpConfigView oidc_config = 8; + } + uint64 sequence = 9; +} + +message OidcIdpConfigView { + string client_id = 1; + string issuer = 2; + repeated string scopes = 3; + OIDCMappingField idp_display_name_mapping = 4; + OIDCMappingField username_mapping = 5; +} + +message IdpSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated IdpSearchQuery queries = 3; +} + +message IdpSearchQuery { + IdpSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2; + string value = 3; +} + +enum IdpSearchKey { + IDPSEARCHKEY_UNSPECIFIED = 0; + IDPSEARCHKEY_IDP_CONFIG_ID = 1; + IDPSEARCHKEY_NAME = 2; + IDPSEARCHKEY_PROVIDER_TYPE = 3; +} + +message LoginPolicy { + bool allow_username_password = 1; + bool allow_register = 2; + bool allow_external_idp = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; + bool force_mfa = 6; + PasswordlessType passwordless_type = 7; +} + +message LoginPolicyRequest { + bool allow_username_password = 1; + bool allow_register = 2; + bool allow_external_idp = 3; + bool force_mfa = 4; + PasswordlessType passwordless_type = 5; +} + +enum PasswordlessType { + PASSWORDLESSTYPE_NOT_ALLOWED = 0; + PASSWORDLESSTYPE_ALLOWED = 1; +} + + +message IdpProviderID { + string idp_config_id = 1 [(validate.rules).string = {min_len: 1}]; +} + +message IdpProviderAdd { + string idp_config_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + IdpProviderType idp_provider_type = 2 [(validate.rules).enum = {not_in: [0]}]; +} + +message IdpProvider { + string idp_config_id = 1; + IdpProviderType idp_provider_Type = 2; +} + +message LoginPolicyView { + bool default = 1; + bool allow_username_password = 2; + bool allow_register = 3; + bool allow_external_idp = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; + bool force_mfa = 7; + PasswordlessType passwordless_type = 8; +} + +message IdpProviderView { + string idp_config_id = 1; + string name = 2; + IdpProviderType type = 3; +} + +enum IdpType { + IDPTYPE_UNSPECIFIED = 0; + IDPTYPE_OIDC = 1; + IDPTYPE_SAML = 2; +} + +enum IdpProviderType { + IDPPROVIDERTYPE_UNSPECIFIED = 0; + IDPPROVIDERTYPE_SYSTEM = 1; + IDPPROVIDERTYPE_ORG = 2; +} + +message IdpProviderSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated IdpProviderView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message IdpProviderSearchRequest { + uint64 offset = 1; + uint64 limit = 2; +} + +//ProjectType is deprecated, remove as soon as console is ready +enum ProjectType { + PROJECTTYPE_UNSPECIFIED = 0; + PROJECTTYPE_OWNED = 1; + PROJECTTYPE_GRANTED = 2; +} + +message ExternalIDPSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + string user_id = 3; +} + +message ExternalIDPSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ExternalIDPView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message ExternalIDPView { + string user_id = 1; + string idp_config_id = 2; + string external_user_id = 3; + string idp_name = 4; + string external_user_display_name = 5; + google.protobuf.Timestamp creation_date = 6; + google.protobuf.Timestamp change_date = 7; +} + +message ExternalIDPRemoveRequest { + string user_id = 1; + string idp_config_id = 2; + string external_user_id = 3; +} + +message SecondFactorsResult { + repeated SecondFactorType second_factors = 1; +} + +message SecondFactor { + SecondFactorType second_factor = 1; +} + +enum SecondFactorType { + SECONDFACTORTYPE_UNSPECIFIED = 0; + SECONDFACTORTYPE_OTP = 1; + SECONDFACTORTYPE_U2F = 2; +} + +message MultiFactorsResult { + repeated MultiFactorType multi_factors = 1; +} + +message MultiFactor { + MultiFactorType multi_factor = 1; +} + +enum MultiFactorType { + MULTIFACTORTYPE_UNSPECIFIED = 0; + MULTIFACTORTYPE_U2F_WITH_PIN = 1; +} + +message PasswordComplexityPolicy { + uint64 min_length = 1; + bool has_lowercase = 2; + bool has_uppercase = 3; + bool has_number = 4; + bool has_symbol = 5; + uint64 sequence = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; +} + +message PasswordComplexityPolicyRequest { + uint64 min_length = 1; + bool has_lowercase = 2; + bool has_uppercase = 3; + bool has_number = 4; + bool has_symbol = 5; +} + +message PasswordComplexityPolicyView { + bool default = 1; + uint64 min_length = 2; + bool has_lowercase = 3; + bool has_uppercase = 4; + bool has_number = 5; + bool has_symbol = 6; + uint64 sequence = 7; + google.protobuf.Timestamp creation_date = 8; + google.protobuf.Timestamp change_date = 9; +} + +message PasswordAgePolicy { + uint64 max_age_days = 1; + uint64 expire_warn_days = 2; + uint64 sequence = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; +} + +message PasswordAgePolicyRequest { + uint64 max_age_days = 1; + uint64 expire_warn_days = 2; +} + +message PasswordAgePolicyView { + bool default = 1; + uint64 max_age_days = 2; + uint64 expire_warn_days = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} + +message PasswordLockoutPolicy { + uint64 max_attempts = 1; + bool show_lockout_failure = 2; + uint64 sequence = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; +} + +message PasswordLockoutPolicyRequest { + uint64 max_attempts = 1; + bool show_lockout_failure = 2; +} + +message PasswordLockoutPolicyView { + bool default = 1; + uint64 max_attempts = 2; + bool show_lockout_failure = 3; + uint64 sequence = 4; + google.protobuf.Timestamp creation_date = 5; + google.protobuf.Timestamp change_date = 6; +} +message MailTemplate { + bytes template = 1; + google.protobuf.Timestamp creation_date = 2; + google.protobuf.Timestamp change_date = 3; +} + +message MailTemplateUpdate { + bytes template = 1; +} + +message MailTemplateView { + bool default = 1; + bytes template = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; +} + +message MailText { + string mail_text_type = 1; + string language = 2; + string title = 3; + string pre_header = 4; + string subject = 5; + string greeting = 6; + string text = 7; + string button_text = 8; + google.protobuf.Timestamp creation_date = 9; + google.protobuf.Timestamp change_date = 10; +} + +message MailTextUpdate { + string mail_text_type = 1; + string language = 2; + string title = 3; + string pre_header = 4; + string subject = 5; + string greeting = 6; + string text = 7; + string button_text = 8; +} + +message MailTextRemove { + string mail_text_type = 1; + string language = 2; +} + +message MailTextsView{ + repeated MailTextView texts = 1; +} + +message MailTextView { + bool default = 1; + string mail_text_type = 2; + string language = 3; + string title = 4; + string pre_header = 5; + string subject = 6; + string greeting = 7; + string text = 8; + string button_text = 9; + google.protobuf.Timestamp creation_date = 10; + google.protobuf.Timestamp change_date = 11; +} + +message LabelPolicy { + string primary_color = 1; + string secondary_color = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + bool hide_login_name_suffix = 5; +} + +message LabelPolicyRequest { + string primary_color = 1; + string secondary_color = 2; + google.protobuf.Timestamp creation_date = 3; + google.protobuf.Timestamp change_date = 4; + bool hide_login_name_suffix = 5; +} + +message LabelPolicyView { + bool default = 1; + string primary_color = 2; + string secondary_color = 3; + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp change_date = 5; + bool hide_login_name_suffix = 6; +} \ No newline at end of file diff --git a/src/Zitadel.Api/proto/message.proto b/src/Zitadel.Api/proto/message.proto new file mode 100644 index 00000000..535f1dee --- /dev/null +++ b/src/Zitadel.Api/proto/message.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package caos.zitadel.api.v1; + +option go_package = "github.com/caos/zitadel/pkg/grpc/message"; + +message ErrorDetail { + string id = 1; + string message = 2; +} + +message LocalizedMessage { + string key = 1; + string localized_message = 2; +} \ No newline at end of file diff --git a/src/Zitadel.Api/protoc-gen-swagger/options/annotations.proto b/src/Zitadel.Api/protoc-gen-swagger/options/annotations.proto new file mode 100644 index 00000000..5151fd5a --- /dev/null +++ b/src/Zitadel.Api/protoc-gen-swagger/options/annotations.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_swagger.options; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"; + +import "google/protobuf/descriptor.proto"; +import "protoc-gen-swagger/options/openapiv2.proto"; + +extend google.protobuf.FileOptions { + // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Swagger openapiv2_swagger = 1042; +} +extend google.protobuf.MethodOptions { + // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Operation openapiv2_operation = 1042; +} +extend google.protobuf.MessageOptions { + // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Schema openapiv2_schema = 1042; +} +extend google.protobuf.ServiceOptions { + // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Tag openapiv2_tag = 1042; +} +extend google.protobuf.FieldOptions { + // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + JSONSchema openapiv2_field = 1042; +} diff --git a/src/Zitadel.Api/protoc-gen-swagger/options/openapiv2.proto b/src/Zitadel.Api/protoc-gen-swagger/options/openapiv2.proto new file mode 100644 index 00000000..389558cd --- /dev/null +++ b/src/Zitadel.Api/protoc-gen-swagger/options/openapiv2.proto @@ -0,0 +1,648 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_swagger.options; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"; + +import "google/protobuf/any.proto"; +import "google/protobuf/struct.proto"; + +// `Swagger` is a representation of OpenAPI v2 specification's Swagger object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: "; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt"; +// }; +// }; +// schemes: HTTPS; +// consumes: "application/json"; +// produces: "application/json"; +// }; +// +message Swagger { + // Specifies the Swagger Specification version being used. It can be + // used by the Swagger UI and other clients to interpret the API listing. The + // value MUST be "2.0". + string swagger = 1; + // Provides metadata about the API. The metadata can be used by the + // clients if needed. + Info info = 2; + // The host (name or ip) serving the API. This MUST be the host only and does + // not include the scheme nor sub-paths. It MAY include a port. If the host is + // not included, the host serving the documentation is to be used (including + // the port). The host does not support path templating. + string host = 3; + // The base path on which the API is served, which is relative to the host. If + // it is not included, the API is served directly under the host. The value + // MUST start with a leading slash (/). The basePath does not support path + // templating. + // Note that using `base_path` does not change the endpoint paths that are + // generated in the resulting Swagger file. If you wish to use `base_path` + // with relatively generated Swagger paths, the `base_path` prefix must be + // manually removed from your `google.api.http` paths and your code changed to + // serve the API from the `base_path`. + string base_path = 4; + enum SwaggerScheme { + UNKNOWN = 0; + HTTP = 1; + HTTPS = 2; + WS = 3; + WSS = 4; + } + // The transfer protocol of the API. Values MUST be from the list: "http", + // "https", "ws", "wss". If the schemes is not included, the default scheme to + // be used is the one used to access the Swagger definition itself. + repeated SwaggerScheme schemes = 5; + // A list of MIME types the APIs can consume. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string consumes = 6; + // A list of MIME types the APIs can produce. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'paths'. + reserved 8; + // field 9 is reserved for 'definitions', which at this time are already + // exposed as and customizable as proto messages. + reserved 9; + // An object to hold responses that can be used across operations. This + // property does not define global responses for all operations. + map responses = 10; + // Security scheme definitions that can be used across the specification. + SecurityDefinitions security_definitions = 11; + // A declaration of which security schemes are applied for the API as a whole. + // The list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). + // Individual operations can override this definition. + repeated SecurityRequirement security = 12; + // field 13 is reserved for 'tags', which are supposed to be exposed as and + // customizable as proto services. TODO(ivucica): add processing of proto + // service objects into OpenAPI v2 Tag objects. + reserved 13; + // Additional external documentation. + ExternalDocumentation external_docs = 14; + map extensions = 15; +} + +// `Operation` is a representation of OpenAPI v2 specification's Operation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject +// +// Example: +// +// service EchoService { +// rpc Echo(SimpleMessage) returns (SimpleMessage) { +// option (google.api.http) = { +// get: "/v1/example/echo/{id}" +// }; +// +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { +// summary: "Get a message."; +// operation_id: "getMessage"; +// tags: "echo"; +// responses: { +// key: "200" +// value: { +// description: "OK"; +// } +// } +// }; +// } +// } +message Operation { + // A list of tags for API documentation control. Tags can be used for logical + // grouping of operations by resources or any other qualifier. + repeated string tags = 1; + // A short summary of what the operation does. For maximum readability in the + // swagger-ui, this field SHOULD be less than 120 characters. + string summary = 2; + // A verbose explanation of the operation behavior. GFM syntax can be used for + // rich text representation. + string description = 3; + // Additional external documentation for this operation. + ExternalDocumentation external_docs = 4; + // Unique string used to identify the operation. The id MUST be unique among + // all operations described in the API. Tools and libraries MAY use the + // operationId to uniquely identify an operation, therefore, it is recommended + // to follow common programming naming conventions. + string operation_id = 5; + // A list of MIME types the operation can consume. This overrides the consumes + // definition at the Swagger Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string consumes = 6; + // A list of MIME types the operation can produce. This overrides the produces + // definition at the Swagger Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'parameters'. + reserved 8; + // The list of possible responses as they are returned from executing this + // operation. + map responses = 9; + // The transfer protocol for the operation. Values MUST be from the list: + // "http", "https", "ws", "wss". The value overrides the Swagger Object + // schemes definition. + repeated string schemes = 10; + // Declares this operation to be deprecated. Usage of the declared operation + // should be refrained. Default value is false. + bool deprecated = 11; + // A declaration of which security schemes are applied for this operation. The + // list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). This + // definition overrides any declared top-level security. To remove a top-level + // security declaration, an empty array can be used. + repeated SecurityRequirement security = 12; + map extensions = 13; +} + +// `Header` is a representation of OpenAPI v2 specification's Header object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject +// +message Header { + // `Description` is a short description of the header. + string description = 1; + // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported. + string type = 2; + // `Format` The extending format for the previously mentioned type. + string format = 3; + // field 4 is reserved for 'items', but in OpenAPI-specific way. + reserved 4; + // field 5 is reserved `Collection Format` Determines the format of the array if type array is used. + reserved 5; + // `Default` Declares the value of the header that the server will use if none is provided. + // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. + // Unlike JSON Schema this value MUST conform to the defined type for the header. + string default = 6; + // field 7 is reserved for 'maximum'. + reserved 7; + // field 8 is reserved for 'exclusiveMaximum'. + reserved 8; + // field 9 is reserved for 'minimum'. + reserved 9; + // field 10 is reserved for 'exclusiveMinimum'. + reserved 10; + // field 11 is reserved for 'maxLength'. + reserved 11; + // field 12 is reserved for 'minLength'. + reserved 12; + // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3. + string pattern = 13; + // field 14 is reserved for 'maxItems'. + reserved 14; + // field 15 is reserved for 'minItems'. + reserved 15; + // field 16 is reserved for 'uniqueItems'. + reserved 16; + // field 17 is reserved for 'enum'. + reserved 17; + // field 18 is reserved for 'multipleOf'. + reserved 18; +} + +// `Response` is a representation of OpenAPI v2 specification's Response object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject +// +message Response { + // `Description` is a short description of the response. + // GFM syntax can be used for rich text representation. + string description = 1; + // `Schema` optionally defines the structure of the response. + // If `Schema` is not provided, it means there is no content to the response. + Schema schema = 2; + // `Headers` A list of headers that are sent with the response. + // `Header` name is expected to be a string in the canonical format of the MIME header key + // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey + map headers = 3; + // `Examples` gives per-mimetype response examples. + // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object + map examples = 4; + map extensions = 5; +} + +// `Info` is a representation of OpenAPI v2 specification's Info object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: "; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt"; +// }; +// }; +// ... +// }; +// +message Info { + // The title of the application. + string title = 1; + // A short description of the application. GFM syntax can be used for rich + // text representation. + string description = 2; + // The Terms of Service for the API. + string terms_of_service = 3; + // The contact information for the exposed API. + Contact contact = 4; + // The license information for the exposed API. + License license = 5; + // Provides the version of the application API (not to be confused + // with the specification version). + string version = 6; + map extensions = 7; +} + +// `Contact` is a representation of OpenAPI v2 specification's Contact object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { +// info: { +// ... +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// ... +// }; +// ... +// }; +// +message Contact { + // The identifying name of the contact person/organization. + string name = 1; + // The URL pointing to the contact information. MUST be in the format of a + // URL. + string url = 2; + // The email address of the contact person/organization. MUST be in the format + // of an email address. + string email = 3; +} + +// `License` is a representation of OpenAPI v2 specification's License object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { +// info: { +// ... +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt"; +// }; +// ... +// }; +// ... +// }; +// +message License { + // The license name used for the API. + string name = 1; + // A URL to the license used for the API. MUST be in the format of a URL. + string url = 2; +} + +// `ExternalDocumentation` is a representation of OpenAPI v2 specification's +// ExternalDocumentation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { +// ... +// external_docs: { +// description: "More about gRPC-Gateway"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// } +// ... +// }; +// +message ExternalDocumentation { + // A short description of the target documentation. GFM syntax can be used for + // rich text representation. + string description = 1; + // The URL for the target documentation. Value MUST be in the format + // of a URL. + string url = 2; +} + +// `Schema` is a representation of OpenAPI v2 specification's Schema object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +message Schema { + JSONSchema json_schema = 1; + // Adds support for polymorphism. The discriminator is the schema property + // name that is used to differentiate between other schema that inherit this + // schema. The property name used MUST be defined at this schema and it MUST + // be in the required property list. When used, the value MUST be the name of + // this schema or any schema that inherits it. + string discriminator = 2; + // Relevant only for Schema "properties" definitions. Declares the property as + // "read only". This means that it MAY be sent as part of a response but MUST + // NOT be sent as part of the request. Properties marked as readOnly being + // true SHOULD NOT be in the required list of the defined schema. Default + // value is false. + bool read_only = 3; + // field 4 is reserved for 'xml'. + reserved 4; + // Additional external documentation for this schema. + ExternalDocumentation external_docs = 5; + // A free-form property to include an example of an instance for this schema. + // Deprecated, please use example_string instead. + google.protobuf.Any example = 6 [ + deprecated = true + ]; + // A free-form property to include a JSON example of this field. This is copied + // verbatim to the output swagger.json. Quotes must be escaped. + string example_string = 7; +} + +// `JSONSchema` represents properties from JSON Schema taken, and as used, in +// the OpenAPI v2 spec. +// +// This includes changes made by OpenAPI v2. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +// See also: https://cswr.github.io/JsonSchema/spec/basic_types/, +// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json +// +// Example: +// +// message SimpleMessage { +// option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = { +// json_schema: { +// title: "SimpleMessage" +// description: "A simple message." +// required: ["id"] +// } +// }; +// +// // Id represents the message identifier. +// string id = 1; [ +// (grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = { +// {description: "The unique identifier of the simple message." +// }]; +// } +// +message JSONSchema { + // field 1 is reserved for '$id', omitted from OpenAPI v2. + reserved 1; + // field 2 is reserved for '$schema', omitted from OpenAPI v2. + reserved 2; + // Ref is used to define an external reference to include in the message. + // This could be a fully qualified proto message reference, and that type must + // be imported into the protofile. If no message is identified, the Ref will + // be used verbatim in the output. + // For example: + // `ref: ".google.protobuf.Timestamp"`. + string ref = 3; + // field 4 is reserved for '$comment', omitted from OpenAPI v2. + reserved 4; + // The title of the schema. + string title = 5; + // A short description of the schema. + string description = 6; + string default = 7; + bool read_only = 8; + // A free-form property to include a JSON example of this field. This is copied + // verbatim to the output swagger.json. Quotes must be escaped. + // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject + string example = 9; + double multiple_of = 10; + // Maximum represents an inclusive upper limit for a numeric instance. The + // value of MUST be a number, + double maximum = 11; + bool exclusive_maximum = 12; + // minimum represents an inclusive lower limit for a numeric instance. The + // value of MUST be a number, + double minimum = 13; + bool exclusive_minimum = 14; + uint64 max_length = 15; + uint64 min_length = 16; + string pattern = 17; + // field 18 is reserved for 'additionalItems', omitted from OpenAPI v2. + reserved 18; + // field 19 is reserved for 'items', but in OpenAPI-specific way. + // TODO(ivucica): add 'items'? + reserved 19; + uint64 max_items = 20; + uint64 min_items = 21; + bool unique_items = 22; + // field 23 is reserved for 'contains', omitted from OpenAPI v2. + reserved 23; + uint64 max_properties = 24; + uint64 min_properties = 25; + repeated string required = 26; + // field 27 is reserved for 'additionalProperties', but in OpenAPI-specific + // way. TODO(ivucica): add 'additionalProperties'? + reserved 27; + // field 28 is reserved for 'definitions', omitted from OpenAPI v2. + reserved 28; + // field 29 is reserved for 'properties', but in OpenAPI-specific way. + // TODO(ivucica): add 'additionalProperties'? + reserved 29; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // patternProperties, dependencies, propertyNames, const + reserved 30 to 33; + // Items in 'array' must be unique. + repeated string array = 34; + + enum JSONSchemaSimpleTypes { + UNKNOWN = 0; + ARRAY = 1; + BOOLEAN = 2; + INTEGER = 3; + NULL = 4; + NUMBER = 5; + OBJECT = 6; + STRING = 7; + } + + repeated JSONSchemaSimpleTypes type = 35; + // `Format` + string format = 36; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: contentMediaType, contentEncoding, if, then, else + reserved 37 to 41; + // field 42 is reserved for 'allOf', but in OpenAPI-specific way. + // TODO(ivucica): add 'allOf'? + reserved 42; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // anyOf, oneOf, not + reserved 43 to 45; + // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1 + repeated string enum = 46; +} + +// `Tag` is a representation of OpenAPI v2 specification's Tag object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject +// +message Tag { + // field 1 is reserved for 'name'. In our generator, this is (to be) extracted + // from the name of proto service, and thus not exposed to the user, as + // changing tag object's name would break the link to the references to the + // tag in individual operation specifications. + // + // TODO(ivucica): Add 'name' property. Use it to allow override of the name of + // global Tag object, then use that name to reference the tag throughout the + // Swagger file. + reserved 1; + // A short description for the tag. GFM syntax can be used for rich text + // representation. + string description = 2; + // Additional external documentation for this tag. + ExternalDocumentation external_docs = 3; +} + +// `SecurityDefinitions` is a representation of OpenAPI v2 specification's +// Security Definitions object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject +// +// A declaration of the security schemes available to be used in the +// specification. This does not enforce the security schemes on the operations +// and only serves to provide the relevant details for each scheme. +message SecurityDefinitions { + // A single security scheme definition, mapping a "name" to the scheme it + // defines. + map security = 1; +} + +// `SecurityScheme` is a representation of OpenAPI v2 specification's +// Security Scheme object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject +// +// Allows the definition of a security scheme that can be used by the +// operations. Supported schemes are basic authentication, an API key (either as +// a header or as a query parameter) and OAuth2's common flows (implicit, +// password, application and access code). +message SecurityScheme { + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + enum Type { + TYPE_INVALID = 0; + TYPE_BASIC = 1; + TYPE_API_KEY = 2; + TYPE_OAUTH2 = 3; + } + + // The location of the API key. Valid values are "query" or "header". + enum In { + IN_INVALID = 0; + IN_QUERY = 1; + IN_HEADER = 2; + } + + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + enum Flow { + FLOW_INVALID = 0; + FLOW_IMPLICIT = 1; + FLOW_PASSWORD = 2; + FLOW_APPLICATION = 3; + FLOW_ACCESS_CODE = 4; + } + + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + Type type = 1; + // A short description for security scheme. + string description = 2; + // The name of the header or query parameter to be used. + // Valid for apiKey. + string name = 3; + // The location of the API key. Valid values are "query" or + // "header". + // Valid for apiKey. + In in = 4; + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + // Valid for oauth2. + Flow flow = 5; + // The authorization URL to be used for this flow. This SHOULD be in + // the form of a URL. + // Valid for oauth2/implicit and oauth2/accessCode. + string authorization_url = 6; + // The token URL to be used for this flow. This SHOULD be in the + // form of a URL. + // Valid for oauth2/password, oauth2/application and oauth2/accessCode. + string token_url = 7; + // The available scopes for the OAuth2 security scheme. + // Valid for oauth2. + Scopes scopes = 8; + map extensions = 9; +} + +// `SecurityRequirement` is a representation of OpenAPI v2 specification's +// Security Requirement object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject +// +// Lists the required security schemes to execute this operation. The object can +// have multiple security schemes declared in it which are all required (that +// is, there is a logical AND between the schemes). +// +// The name used for each property MUST correspond to a security scheme +// declared in the Security Definitions. +message SecurityRequirement { + // If the security scheme is of type "oauth2", then the value is a list of + // scope names required for the execution. For other security scheme types, + // the array MUST be empty. + message SecurityRequirementValue { + repeated string scope = 1; + } + // Each name must correspond to a security scheme which is declared in + // the Security Definitions. If the security scheme is of type "oauth2", + // then the value is a list of scope names required for the execution. + // For other security scheme types, the array MUST be empty. + map security_requirement = 1; +} + +// `Scopes` is a representation of OpenAPI v2 specification's Scopes object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject +// +// Lists the available scopes for an OAuth2 security scheme. +message Scopes { + // Maps between a name of a scope to a short description of it (as the value + // of the property). + map scope = 1; +} diff --git a/src/Zitadel.Api/validate/validate.proto b/src/Zitadel.Api/validate/validate.proto new file mode 100644 index 00000000..4195ecf9 --- /dev/null +++ b/src/Zitadel.Api/validate/validate.proto @@ -0,0 +1,863 @@ +syntax = "proto2"; +package validate; + +option go_package = "github.com/envoyproxy/protoc-gen-validate/validate"; +option java_package = "io.envoyproxy.pgv.validate"; + +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +// Validation rules applied at the message level +extend google.protobuf.MessageOptions { + // Disabled nullifies any validation rules for this message, including any + // message fields associated with it that do support validation. + optional bool disabled = 1071; + // Ignore skips generation of validation methods for this message. + optional bool ignored = 1072; +} + +// Validation rules applied at the oneof level +extend google.protobuf.OneofOptions { + // Required ensures that exactly one the field options in a oneof is set; + // validation fails if no fields in the oneof are set. + optional bool required = 1071; +} + +// Validation rules applied at the field level +extend google.protobuf.FieldOptions { + // Rules specify the validations to be performed on this field. By default, + // no validation is performed against a field. + optional FieldRules rules = 1071; +} + +// FieldRules encapsulates the rules for each type of field. Depending on the +// field, the correct set should be used to ensure proper validations. +message FieldRules { + optional MessageRules message = 17; + oneof type { + // Scalar Field Types + FloatRules float = 1; + DoubleRules double = 2; + Int32Rules int32 = 3; + Int64Rules int64 = 4; + UInt32Rules uint32 = 5; + UInt64Rules uint64 = 6; + SInt32Rules sint32 = 7; + SInt64Rules sint64 = 8; + Fixed32Rules fixed32 = 9; + Fixed64Rules fixed64 = 10; + SFixed32Rules sfixed32 = 11; + SFixed64Rules sfixed64 = 12; + BoolRules bool = 13; + StringRules string = 14; + BytesRules bytes = 15; + + // Complex Field Types + EnumRules enum = 16; + RepeatedRules repeated = 18; + MapRules map = 19; + + // Well-Known Field Types + AnyRules any = 20; + DurationRules duration = 21; + TimestampRules timestamp = 22; + } +} + +// FloatRules describes the constraints applied to `float` values +message FloatRules { + // Const specifies that this field must be exactly the specified value + optional float const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional float lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional float lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional float gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional float gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated float in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated float not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// DoubleRules describes the constraints applied to `double` values +message DoubleRules { + // Const specifies that this field must be exactly the specified value + optional double const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional double lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional double lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional double gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional double gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated double in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated double not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Int32Rules describes the constraints applied to `int32` values +message Int32Rules { + // Const specifies that this field must be exactly the specified value + optional int32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional int32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional int32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional int32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional int32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated int32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Int64Rules describes the constraints applied to `int64` values +message Int64Rules { + // Const specifies that this field must be exactly the specified value + optional int64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional int64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional int64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional int64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional int64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated int64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// UInt32Rules describes the constraints applied to `uint32` values +message UInt32Rules { + // Const specifies that this field must be exactly the specified value + optional uint32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional uint32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional uint32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional uint32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional uint32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated uint32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated uint32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// UInt64Rules describes the constraints applied to `uint64` values +message UInt64Rules { + // Const specifies that this field must be exactly the specified value + optional uint64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional uint64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional uint64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional uint64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional uint64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated uint64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated uint64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SInt32Rules describes the constraints applied to `sint32` values +message SInt32Rules { + // Const specifies that this field must be exactly the specified value + optional sint32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sint32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sint32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sint32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sint32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sint32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sint32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SInt64Rules describes the constraints applied to `sint64` values +message SInt64Rules { + // Const specifies that this field must be exactly the specified value + optional sint64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sint64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sint64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sint64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sint64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sint64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sint64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Fixed32Rules describes the constraints applied to `fixed32` values +message Fixed32Rules { + // Const specifies that this field must be exactly the specified value + optional fixed32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional fixed32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional fixed32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional fixed32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional fixed32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated fixed32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated fixed32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Fixed64Rules describes the constraints applied to `fixed64` values +message Fixed64Rules { + // Const specifies that this field must be exactly the specified value + optional fixed64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional fixed64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional fixed64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional fixed64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional fixed64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated fixed64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated fixed64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SFixed32Rules describes the constraints applied to `sfixed32` values +message SFixed32Rules { + // Const specifies that this field must be exactly the specified value + optional sfixed32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sfixed32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sfixed32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sfixed32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sfixed32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sfixed32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sfixed32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SFixed64Rules describes the constraints applied to `sfixed64` values +message SFixed64Rules { + // Const specifies that this field must be exactly the specified value + optional sfixed64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sfixed64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sfixed64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sfixed64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sfixed64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sfixed64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sfixed64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// BoolRules describes the constraints applied to `bool` values +message BoolRules { + // Const specifies that this field must be exactly the specified value + optional bool const = 1; +} + +// StringRules describe the constraints applied to `string` values +message StringRules { + // Const specifies that this field must be exactly the specified value + optional string const = 1; + + // Len specifies that this field must be the specified number of + // characters (Unicode code points). Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 len = 19; + + // MinLen specifies that this field must be the specified number of + // characters (Unicode code points) at a minimum. Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 min_len = 2; + + // MaxLen specifies that this field must be the specified number of + // characters (Unicode code points) at a maximum. Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 max_len = 3; + + // LenBytes specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 len_bytes = 20; + + // MinBytes specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 min_bytes = 4; + + // MaxBytes specifies that this field must be the specified number of bytes + // at a maximum + optional uint64 max_bytes = 5; + + // Pattern specifes that this field must match against the specified + // regular expression (RE2 syntax). The included expression should elide + // any delimiters. + optional string pattern = 6; + + // Prefix specifies that this field must have the specified substring at + // the beginning of the string. + optional string prefix = 7; + + // Suffix specifies that this field must have the specified substring at + // the end of the string. + optional string suffix = 8; + + // Contains specifies that this field must have the specified substring + // anywhere in the string. + optional string contains = 9; + + // NotContains specifies that this field cannot have the specified substring + // anywhere in the string. + optional string not_contains = 23; + + // In specifies that this field must be equal to one of the specified + // values + repeated string in = 10; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated string not_in = 11; + + // WellKnown rules provide advanced constraints against common string + // patterns + oneof well_known { + // Email specifies that the field must be a valid email address as + // defined by RFC 5322 + bool email = 12; + + // Hostname specifies that the field must be a valid hostname as + // defined by RFC 1034. This constraint does not support + // internationalized domain names (IDNs). + bool hostname = 13; + + // Ip specifies that the field must be a valid IP (v4 or v6) address. + // Valid IPv6 addresses should not include surrounding square brackets. + bool ip = 14; + + // Ipv4 specifies that the field must be a valid IPv4 address. + bool ipv4 = 15; + + // Ipv6 specifies that the field must be a valid IPv6 address. Valid + // IPv6 addresses should not include surrounding square brackets. + bool ipv6 = 16; + + // Uri specifies that the field must be a valid, absolute URI as defined + // by RFC 3986 + bool uri = 17; + + // UriRef specifies that the field must be a valid URI as defined by RFC + // 3986 and may be relative or absolute. + bool uri_ref = 18; + + // Address specifies that the field must be either a valid hostname as + // defined by RFC 1034 (which does not support internationalized domain + // names or IDNs), or it can be a valid IP (v4 or v6). + bool address = 21; + + // Uuid specifies that the field must be a valid UUID as defined by + // RFC 4122 + bool uuid = 22; + + // WellKnownRegex specifies a common well known pattern defined as a regex. + KnownRegex well_known_regex = 24; + } + + // This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable + // strict header validation. + // By default, this is true, and HTTP header validations are RFC-compliant. + // Setting to false will enable a looser validations that only disallows + // \r\n\0 characters, which can be used to bypass header matching rules. + optional bool strict = 25 [default = true]; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 26; +} + +// WellKnownRegex contain some well-known patterns. +enum KnownRegex { + UNKNOWN = 0; + + // HTTP header name as defined by RFC 7230. + HTTP_HEADER_NAME = 1; + + // HTTP header value as defined by RFC 7230. + HTTP_HEADER_VALUE = 2; +} + +// BytesRules describe the constraints applied to `bytes` values +message BytesRules { + // Const specifies that this field must be exactly the specified value + optional bytes const = 1; + + // Len specifies that this field must be the specified number of bytes + optional uint64 len = 13; + + // MinLen specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 min_len = 2; + + // MaxLen specifies that this field must be the specified number of bytes + // at a maximum + optional uint64 max_len = 3; + + // Pattern specifes that this field must match against the specified + // regular expression (RE2 syntax). The included expression should elide + // any delimiters. + optional string pattern = 4; + + // Prefix specifies that this field must have the specified bytes at the + // beginning of the string. + optional bytes prefix = 5; + + // Suffix specifies that this field must have the specified bytes at the + // end of the string. + optional bytes suffix = 6; + + // Contains specifies that this field must have the specified bytes + // anywhere in the string. + optional bytes contains = 7; + + // In specifies that this field must be equal to one of the specified + // values + repeated bytes in = 8; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated bytes not_in = 9; + + // WellKnown rules provide advanced constraints against common byte + // patterns + oneof well_known { + // Ip specifies that the field must be a valid IP (v4 or v6) address in + // byte format + bool ip = 10; + + // Ipv4 specifies that the field must be a valid IPv4 address in byte + // format + bool ipv4 = 11; + + // Ipv6 specifies that the field must be a valid IPv6 address in byte + // format + bool ipv6 = 12; + } + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 14; +} + +// EnumRules describe the constraints applied to enum values +message EnumRules { + // Const specifies that this field must be exactly the specified value + optional int32 const = 1; + + // DefinedOnly specifies that this field must be only one of the defined + // values for this enum, failing on any undefined value. + optional bool defined_only = 2; + + // In specifies that this field must be equal to one of the specified + // values + repeated int32 in = 3; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int32 not_in = 4; +} + +// MessageRules describe the constraints applied to embedded message values. +// For message-type fields, validation is performed recursively. +message MessageRules { + // Skip specifies that the validation rules of this field should not be + // evaluated + optional bool skip = 1; + + // Required specifies that this field must be set + optional bool required = 2; +} + +// RepeatedRules describe the constraints applied to `repeated` values +message RepeatedRules { + // MinItems specifies that this field must have the specified number of + // items at a minimum + optional uint64 min_items = 1; + + // MaxItems specifies that this field must have the specified number of + // items at a maximum + optional uint64 max_items = 2; + + // Unique specifies that all elements in this field must be unique. This + // contraint is only applicable to scalar and enum types (messages are not + // supported). + optional bool unique = 3; + + // Items specifies the contraints to be applied to each item in the field. + // Repeated message fields will still execute validation against each item + // unless skip is specified here. + optional FieldRules items = 4; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 5; +} + +// MapRules describe the constraints applied to `map` values +message MapRules { + // MinPairs specifies that this field must have the specified number of + // KVs at a minimum + optional uint64 min_pairs = 1; + + // MaxPairs specifies that this field must have the specified number of + // KVs at a maximum + optional uint64 max_pairs = 2; + + // NoSparse specifies values in this field cannot be unset. This only + // applies to map's with message value types. + optional bool no_sparse = 3; + + // Keys specifies the constraints to be applied to each key in the field. + optional FieldRules keys = 4; + + // Values specifies the constraints to be applied to the value of each key + // in the field. Message values will still have their validations evaluated + // unless skip is specified here. + optional FieldRules values = 5; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 6; +} + +// AnyRules describe constraints applied exclusively to the +// `google.protobuf.Any` well-known type +message AnyRules { + // Required specifies that this field must be set + optional bool required = 1; + + // In specifies that this field's `type_url` must be equal to one of the + // specified values. + repeated string in = 2; + + // NotIn specifies that this field's `type_url` must not be equal to any of + // the specified values. + repeated string not_in = 3; +} + +// DurationRules describe the constraints applied exclusively to the +// `google.protobuf.Duration` well-known type +message DurationRules { + // Required specifies that this field must be set + optional bool required = 1; + + // Const specifies that this field must be exactly the specified value + optional google.protobuf.Duration const = 2; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional google.protobuf.Duration lt = 3; + + // Lt specifies that this field must be less than the specified value, + // inclusive + optional google.protobuf.Duration lte = 4; + + // Gt specifies that this field must be greater than the specified value, + // exclusive + optional google.protobuf.Duration gt = 5; + + // Gte specifies that this field must be greater than the specified value, + // inclusive + optional google.protobuf.Duration gte = 6; + + // In specifies that this field must be equal to one of the specified + // values + repeated google.protobuf.Duration in = 7; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated google.protobuf.Duration not_in = 8; +} + +// TimestampRules describe the constraints applied exclusively to the +// `google.protobuf.Timestamp` well-known type +message TimestampRules { + // Required specifies that this field must be set + optional bool required = 1; + + // Const specifies that this field must be exactly the specified value + optional google.protobuf.Timestamp const = 2; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional google.protobuf.Timestamp lt = 3; + + // Lte specifies that this field must be less than the specified value, + // inclusive + optional google.protobuf.Timestamp lte = 4; + + // Gt specifies that this field must be greater than the specified value, + // exclusive + optional google.protobuf.Timestamp gt = 5; + + // Gte specifies that this field must be greater than the specified value, + // inclusive + optional google.protobuf.Timestamp gte = 6; + + // LtNow specifies that this must be less than the current time. LtNow + // can only be used with the Within rule. + optional bool lt_now = 7; + + // GtNow specifies that this must be greater than the current time. GtNow + // can only be used with the Within rule. + optional bool gt_now = 8; + + // Within specifies that this field must be within this duration of the + // current time. This constraint can be used alone or with the LtNow and + // GtNow rules. + optional google.protobuf.Duration within = 9; +} diff --git a/src/Zitadel/Authentication/Credentials/ServiceAccount.cs b/src/Zitadel/Authentication/Credentials/ServiceAccount.cs new file mode 100644 index 00000000..c31a27ec --- /dev/null +++ b/src/Zitadel/Authentication/Credentials/ServiceAccount.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Jose; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security; + +namespace Zitadel.Authentication.Credentials +{ + /// + /// + /// A Zitadel can be loaded from a json file + /// and helps with authentication on a Zitadel IAM. + /// + /// + /// The mechanism is defined here: + /// Zitadel Docs. + /// + /// + public record ServiceAccount + { + private static readonly HttpClient HttpClient = new(); + + /// + /// The key type. + /// + public const string Type = "serviceaccount"; + + /// + /// The user id associated with this service account. + /// + public string UserId { get; init; } = string.Empty; + + /// + /// This is unique ID (on Zitadel) of the key. + /// + public string KeyId { get; init; } = string.Empty; + + /// + /// The private key generated by Zitadel for this . + /// + public string Key { get; init; } = string.Empty; + + /// + /// Load a from a file at a given (relative or absolute) path. + /// + /// The relative or absolute filepath to the json file. + /// The parsed . + /// When the file does not exist. + /// When the deserializer returns 'null'. + /// + /// Thrown when the JSON is invalid, + /// the type is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + public static async Task LoadFromJsonFileAsync(string pathToJson) + { + var path = Path.GetFullPath( + Path.IsPathRooted(pathToJson) + ? pathToJson + : Path.Join(Directory.GetCurrentDirectory(), pathToJson)); + + if (!File.Exists(path)) + { + throw new FileNotFoundException($"File not found: {path}.", path); + } + + await using var stream = File.OpenRead(path); + return await LoadFromJsonStreamAsync(stream); + } + + /// + public static ServiceAccount LoadFromJsonFile(string pathToJson) => LoadFromJsonFileAsync(pathToJson).Result; + + /// + /// Load a from a given stream (FileStream, MemoryStream, ...). + /// + /// The stream to read the json from. + /// The parsed . + /// When the deserializer returns 'null'. + /// + /// Thrown when the JSON is invalid, + /// the type is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + public static async Task LoadFromJsonStreamAsync(Stream stream) => + await JsonSerializer.DeserializeAsync( + stream, + new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) ?? + throw new InvalidDataException("The json file yielded a 'null' result for deserialization."); + + /// + public static ServiceAccount LoadFromJsonStream(Stream stream) => LoadFromJsonStreamAsync(stream).Result; + + /// + /// Load a from a string that contains json. + /// + /// Json string. + /// The parsed . + /// When the deserializer returns 'null'. + /// + /// Thrown when the JSON is invalid, + /// the type is not compatible with the JSON, + /// or when there is remaining data in the Stream. + /// + public static async Task LoadFromJsonStringAsync(string json) + { + await using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json), 0, json.Length); + return await LoadFromJsonStreamAsync(memoryStream); + } + + /// + public static ServiceAccount LoadFromJsonString(string json) => LoadFromJsonStringAsync(json).Result; + + /// + /// Authenticate the given service account against the issuer in the options. + /// As example, this token can be used to communicate with API applications or + /// with the Zitadel API itself. + /// + /// that contain the parameters for the authentication. + /// An opaque access token which can be used to communciate with relying parties. + public async Task AuthenticateAsync(AuthOptions authOptions) + { + var manager = new ConfigurationManager( + DiscoveryEndpoint(authOptions.DiscoveryEndpoint ?? authOptions.Endpoint), + new OpenIdConnectConfigurationRetriever(), + new HttpDocumentRetriever(HttpClient)); + + var oidcConfig = await manager.GetConfigurationAsync(); + + var jwt = await GetSignedJwtAsync(authOptions.Endpoint); + var request = new HttpRequestMessage(HttpMethod.Post, oidcConfig.TokenEndpoint) + { + Content = new FormUrlEncodedContent( + new[] + { + new KeyValuePair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"), + new KeyValuePair( + "assertion", + $"{jwt}"), + new KeyValuePair("scope", authOptions.CreateOidcScopes()), + }), + }; + + var response = await HttpClient.SendAsync(request); + var token = await response + .EnsureSuccessStatusCode() + .Content + .ReadFromJsonAsync(); + + return token?.AccessToken ?? throw new("Access token could not be parsed."); + } + + /// + public string Authenticate(AuthOptions authOptions) => AuthenticateAsync(authOptions).Result; + + private static string DiscoveryEndpoint(string discoveryEndpoint) => + discoveryEndpoint.EndsWith(ZitadelDefaults.DiscoveryEndpointPath) + ? discoveryEndpoint + : discoveryEndpoint.TrimEnd('/') + ZitadelDefaults.DiscoveryEndpointPath; + + private string GetSignedJwt(string issuer) => GetSignedJwtAsync(issuer).Result; + + private async Task GetSignedJwtAsync(string issuer) + { + using var rsa = new RSACryptoServiceProvider(); + rsa.ImportParameters(await GetRsaParametersAsync()); + + return JWT.Encode( + new Dictionary + { + { "iss", UserId }, + { "sub", UserId }, + { "iat", DateTimeOffset.Now.ToUnixTimeSeconds() }, + { "exp", ((DateTimeOffset)DateTime.Now.AddMinutes(1)).ToUnixTimeSeconds() }, + { "aud", issuer }, + }, + rsa, + JwsAlgorithm.RS256, + new Dictionary + { + { "kid", KeyId }, + }); + } + + private async Task GetRsaParametersAsync() + { + var bytes = Encoding.UTF8.GetBytes(Key); + await using var ms = new MemoryStream(bytes); + using var sr = new StreamReader(ms); + var pemReader = new PemReader(sr); + + if (!(pemReader.ReadObject() is AsymmetricCipherKeyPair keyPair)) + { + throw new("RSA Keypair could not be read."); + } + + return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); + } + + private record AccessTokenResponse + { + [JsonPropertyName("access_token")] + public string AccessToken { get; init; } = string.Empty; + } + + /// + /// Options for the authentication with a . + /// + public record AuthOptions + { + /// + /// Endpoint which will be called on the "token endpoint" to get an access token. + /// Defaults to . + /// + public string Endpoint { get; init; } = ZitadelDefaults.Issuer; + + /// + /// If set, overwrites the discovery endpoint for . + /// This may be used, if the discovery endpoint is not on the well-known url + /// of the endpoint. Beware that the well-known part ("/.well-known/openid-configuration") + /// is still attached to the url. + /// + public string? DiscoveryEndpoint { get; init; } + + /// + /// If set, the scope for a primary domain ("urn:zitadel:iam:org:domain:primary:{PrimaryDomain}") + /// will be attached to the list of scopes for the access token of the service account. + /// + public string? PrimaryDomain { get; init; } + + /// + /// Set a list of roles that must be attached to this service account to be + /// successfully authenticated. Translates to the role scope ("urn:zitadel:iam:org:project:role:{Role}"). + /// + public IList RequiredRoles { get; init; } = new List(); + + /// + /// Set a list of audiences that are attached to the returned access token. + /// Translates to the additional audience scope ("urn:zitadel:iam:org:project:id:{ProjectId}:aud"). + /// + public IList ProjectAudiences { get; init; } = new List(); + + /// + /// List of arbitrary additional scopes that are concatenated into the scope. + /// + public IList AdditionalScopes { get; init; } = new List(); + + internal string CreateOidcScopes() => + string.Join( + ' ', + new[] + { + "openid", + PrimaryDomain != null + ? $"urn:zitadel:iam:org:domain:primary:{PrimaryDomain}" + : string.Empty, + } + .Union(AdditionalScopes) + .Union(ProjectAudiences.Select(p => $"urn:zitadel:iam:org:project:id:{p}:aud")) + .Union(RequiredRoles.Select(r => $"urn:zitadel:iam:org:project:role:{r}")) + .Where(s => !string.IsNullOrWhiteSpace(s))); + } + } +} diff --git a/src/Zitadel/Authentication/ZitadelDefaults.cs b/src/Zitadel/Authentication/ZitadelDefaults.cs index 66606b7f..8b1e0fbb 100644 --- a/src/Zitadel/Authentication/ZitadelDefaults.cs +++ b/src/Zitadel/Authentication/ZitadelDefaults.cs @@ -1,4 +1,5 @@ -using Zitadel.Authorization; +using Zitadel.Authentication.Credentials; +using Zitadel.Authorization; namespace Zitadel.Authentication { @@ -66,6 +67,23 @@ public static class ZitadelDefaults /// public const string PrimaryDomainClaimName = "urn:zitadel:iam:org:domain:primary"; + /// + /// This is the project id for the zitadel API of Zitadel.ch. + /// This is needed for accessing the official zitadel API via grpc / rest endpoint + /// when using a to authenticate. + /// + public const string ZitadelApiProjectId = "69234237810729019"; + + /// + /// The endpoint (url) of the official zitadel API. + /// + public const string ZitadelApiEndpoint = "https://api.zitadel.ch"; + + /// + /// Header which is used to provide context to grpc/rest api calls. + /// + public const string ZitadelOrgIdHeader = "x-zitadel-orgid"; + /// /// Constructor for organisation specific role claims. /// They are used to specify roles on a specific organization. diff --git a/src/Zitadel/Zitadel.csproj b/src/Zitadel/Zitadel.csproj index 38cff877..d74a390b 100644 --- a/src/Zitadel/Zitadel.csproj +++ b/src/Zitadel/Zitadel.csproj @@ -4,8 +4,8 @@ - net5.0 true + net5.0 @@ -28,7 +28,11 @@ - + + true + \ + false + diff --git a/tests/Zitadel.Api.Access.Dev/Program.cs b/tests/Zitadel.Api.Access.Dev/Program.cs new file mode 100644 index 00000000..97ef52d1 --- /dev/null +++ b/tests/Zitadel.Api.Access.Dev/Program.cs @@ -0,0 +1,25 @@ +using System; +using Zitadel.Api; +using Zitadel.Authentication; +using Zitadel.Authentication.Credentials; + +Console.WriteLine("Login with Zitadel Service Account"); +var sa = await ServiceAccount.LoadFromJsonFileAsync("./service-account.json"); +var api = Clients.ManagementService( + new() + { + Endpoint = ZitadelDefaults.ZitadelApiEndpoint, + Organization = "69234230193872955", + ServiceAccountAuthentication = (sa, new() + { + ProjectAudiences = { ZitadelDefaults.ZitadelApiProjectId }, + }), + }); +Console.WriteLine("Fetch Roles from Zitadel API"); +var roles = await api.SearchProjectRolesAsync( + new() { ProjectId = "84856448403694484" }); +Console.WriteLine("Roles:"); +foreach (var r in roles.Result) +{ + Console.WriteLine($"{r.Key} : {r.DisplayName} : {r.Group}"); +} diff --git a/tests/Zitadel.Api.Access.Dev/Zitadel.Api.Access.Dev.csproj b/tests/Zitadel.Api.Access.Dev/Zitadel.Api.Access.Dev.csproj new file mode 100644 index 00000000..bd3d7a16 --- /dev/null +++ b/tests/Zitadel.Api.Access.Dev/Zitadel.Api.Access.Dev.csproj @@ -0,0 +1,19 @@ + + + + Exe + net5.0 + + + + + + + + + + PreserveNewest + + + + diff --git a/tests/Zitadel.Api.Access.Dev/service-account.json b/tests/Zitadel.Api.Access.Dev/service-account.json new file mode 100644 index 00000000..b6e85660 --- /dev/null +++ b/tests/Zitadel.Api.Access.Dev/service-account.json @@ -0,0 +1,6 @@ +{ + "type": "serviceaccount", + "keyId": "100820491081673449", + "key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAtxkh+++/afNlrpVQxCtcfwDtWtc4MwF/SfW3TNuXgnNP52MG\ni3Gb4voLJHh+IjuLubDt2GSkFsvRmdqNU8rYF5ATtVnSwaA8I76FZwHpTYPRqM4j\nrUjCQTv8V+1xKlzDANn0pq+aJuUwP89TmOgk+S5yKHdc9Am27SxFxXlIjeyr/t//\nQE4BLp8BLjbWqb04s9kWUdZhnPyvrjekmO2F0P58xUKq7Of7wgbA7aSY610TSzMj\nyZpToByO3NSaLOIQEmgiXMGXIrH1SeGvZ2YWe2ONoEp1RJBel2MvcfzOWIHWtsfG\nS8FCnq/tS4kTQaNvcSgv1OHHIaST11rZ7ETzNwIDAQABAoIBAQCNAmYRyMn5puGg\nWo4hBePuS6CAXU+CuJx3MS1Bm7xLUtMuuCzKJ2yOmVMLe2BorMMm9wO1q4xTohSC\nELbW2VkijGshN1QAmo62WHwUoHw0nlhRULBZO7ee1bu5W7vTjC0B8tlfpc1qNwe5\nnMoc8GvJG+P/Fi4YI4MllPsG4ijmYxfJKQcw5EZofW53RGdYsxBeAn6AHeK1To9/\nMVidEEQFHpDP4S5sE+oE8PAmy4FXpTUkCHqK0hj9Iyn1MvqzYBA9AkPJHkDOgMIG\nu2yH4ld1+BqPOFaD2i9+n++DS7PuD0k5GAZnHVNvOef7p0zzcf7pU8HvQ81rY+en\nP5yj58BBAoGBAMPIajJkhJOqCVSTEK+OobNeq/htdGk0A0soj1X11Oj8OdQqcmti\nRsAib4AGsuZ232dCQktHrRgehvbOX11KawvNm6IHVV55v8BdXaFNPknUszduMUH/\nRZKzrIJbvHmJh+A1NLMS5gGJ27pLuOBEuQMuqjlgbz/wnFJgy1UyNjtrAoGBAO9p\n8rwhj8WBUawtK8JomOXw5WT4Pj+Q2tq78fSQd4RBN5+lJoX/PHMMFB+hYG5Pc5Wr\ntQB/6jXer/k8h0EnT1rLLVBaxMAfTRivVrbN6sZmlFX/CbYcLyWFF0yefg2Bf6ro\ny8N+JJq7vENIcxzRqoCSakQvWzaRuGznsWFoMQZlAoGBALQa7V7YS4IP/S9ZbxMh\nUi3RHMCX6GxTVehRMrLwSGHgjk5YkhOpP87Gnu4Wu4L5vgCe2p+oG1tB0gS/ysjm\nid080zxWJLmZpMWQ3m5hwbhnvEp0y0mcTONs+HifbZ4c3+to5scJzp+Bs6oIuNVP\nQyfjSOMpjI40iYna05LXcz5rAoGBAK1kJhBqnh2EkwoHjPG3n0SpOvntb+fJmTdh\ndtKQ7n4yXX6ZtVTGjZkk3k4qKhS6JhP3z5cVDSMGrJvtYmeleKthCuhvVJ9wC/4c\nvsiWSbp3AGdn1ji2IM8jnMQ45UISz8uP3kMvUINgA/EWv4fXsYx8fcn0IjNbRh5Z\njvXk59yhAoGAWvPOyX+g02Oi4958WpOyG3hU5XxPUxWB1FMTsusatARwXOwbbl92\n5bBgz7eb4wXhfUf7U3sItIH4Kf7xSrZwwq2cbTfBUE7k78kPhYOaJQD7LxxmdE98\n44Kc2VWK82jZIhgcswjRuuxpTMzWB89mC5UQtzykK5/m7+LLbOMdtHg=\n-----END RSA PRIVATE KEY-----\n", + "userId": "85328809577999392" +} diff --git a/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs b/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs index 586ac770..f97e98d9 100644 --- a/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs +++ b/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs @@ -1,8 +1,10 @@ -using System.Net.Http.Json; +using System.Net.Http.Headers; +using System.Net.Http.Json; using System.Threading.Tasks; using FluentAssertions; using Microsoft.AspNetCore.Http; using Xunit; +using Zitadel.Authentication.Credentials; using Zitadel.Test.WebFactories; namespace Zitadel.Test.Authentication @@ -35,12 +37,19 @@ public async Task Should_Return_Unauthorized_Without_Any_Token() result.StatusCode.Should().Be(StatusCodes.Status401Unauthorized); } - [Fact(Skip = "Until a solution for 'get an access token' is found.")] - public async Task Should_Return_Data_With_Jwt_Token() + [Fact] + public async Task Should_Return_Data_With_Token() { + var sa = await ServiceAccount.LoadFromJsonFileAsync("service-account.json"); + var token = await sa.AuthenticateAsync( + new() + { + ProjectAudiences = { "84856448403694484" }, + }); var client = _factory.CreateClient(); + client.DefaultRequestHeaders.Authorization = new("Bearer", token); var result = await client.GetAsync("/authed"); - result.StatusCode.Should().Be(StatusCodes.Status401Unauthorized); + result.StatusCode.Should().Be(StatusCodes.Status200OK); } } } diff --git a/tests/Zitadel.Test/WebFactories/AuthenticationHandlerWebFactory.cs b/tests/Zitadel.Test/WebFactories/AuthenticationHandlerWebFactory.cs index a4c749aa..7e710c5a 100644 --- a/tests/Zitadel.Test/WebFactories/AuthenticationHandlerWebFactory.cs +++ b/tests/Zitadel.Test/WebFactories/AuthenticationHandlerWebFactory.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Zitadel.Authentication; +using Zitadel.Authentication.Credentials; namespace Zitadel.Test.WebFactories { @@ -21,7 +22,13 @@ public void ConfigureServices(IServiceCollection services) services .AddAuthorization() .AddAuthentication(ZitadelDefaults.ApiAuthenticationScheme) - .AddZitadelApi(o => o.ClientId = "84891356119558811@zitadel_net"); + .AddZitadelApi( + o => + { + o.ClientId = "100962076121492209@zitadel_net"; + o.JwtProfileKey = new JwtPrivateKeyPath("api-application.json"); + o.ValidAudiences = new[] { "84856448403694484" }; + }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/tests/Zitadel.Test/Zitadel.Test.csproj b/tests/Zitadel.Test/Zitadel.Test.csproj index 0c905b03..95471b08 100644 --- a/tests/Zitadel.Test/Zitadel.Test.csproj +++ b/tests/Zitadel.Test/Zitadel.Test.csproj @@ -25,4 +25,10 @@ + + + PreserveNewest + + + diff --git a/tests/Zitadel.Test/api-application.json b/tests/Zitadel.Test/api-application.json new file mode 100644 index 00000000..d6cf6e7a --- /dev/null +++ b/tests/Zitadel.Test/api-application.json @@ -0,0 +1 @@ +{"type":"application","keyId":"100962085483110428","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAuAz+zzcAnsaSyFdWxyR+fMWAHmFB03Csn/Ggvgvum1rH5emD\niA++hs7m/8HLI4xQDVXkCjenq8RezAIpcJAz9ntSHLeMSLH838k25d5fwNSxx4CN\nKBRrev2L930undntGZ2Z7mhCfqKprxPLzdGgIcZHPmduTQ8CQC/nYRWsdG6EcG8d\n4/Jx/OPLsZlIMtgVr3tpxgaoi1TduAWG1lhD30zqBhrt7EB0OynDq5h7me6ogY6M\nGxIJHUerIBRnADv3QhAVOEiKHwUi3wLR9oJmob7uW2KQrHkawnIpmnHPJTQGA4hH\nTIJJbEmBzAQYTrbE3OrVbrtTaCPx5UckTaSM/QIDAQABAoIBAE0ozY5rnavyzciF\nrRCxwQFn/y09Us5k10n6j4v9Dpw8s780ZXseNp7nkLo6MYKGqWzdzEtoR7fgDGn0\nohDZs58q1iLJ5Fr5m2zqO0SlNHlv8dQUlZju7pxSPWKVLXzpxtfNVaI4PWA/q2mF\noBR6RGrEaEvGKtzNZQEp1BPEdVcd4/QerxeGdRzJLXvzd3k84W7/k8RavKMDdIl1\nRoWrQDHlEicQmj/Nj9yPPm0MDNMlfTAbRnruppFRL5aKDtA9oCVjReHA+HqE+yU0\ngiavfy2GVnbtAqZtLrNoIQaSCIwfm5zR0gCHu4OjB2fkXOE9uwozScRRHGBgsK0n\nuP/BwEECgYEA0X4+vi/98Z61oYTFdYtmX2H91Guozu88qRDYPkaKjsZAHJBdzH2P\nSWOrXv/la0iwC8XoFbEv6WjiL1jblP8/mol/eYcQTBOCUYQjWDYt9LIV+5EquX0k\n6WBKGWgGye7eX2hjobdr+gGXzUKy4s+YQE07UE/w1Sli6l/jcClqgy0CgYEA4OjU\nBCWKVMmSZCg3N/naztZDUEXydxj1VZA/hKHwCNogRDrHkDbkFOXVHKcODQkm1Kko\n7k9fpTdgLGLFyq/RAQRz16QmBjzOUjSu+W/1uViD1i67Ry2KnZeTXp3IpGH3ntTJ\ncFkOe6mVJ+N3fziGD66zYxiiGrg+vc1MdtibkxECgYEAl6kymRM7X7GLQnxRSzB5\nE3JohWDPW8g8fRTRx/vfkU8a2NL25PS9R98dEHBnUUzVDiDfTNzyl6KzpJ1Kkkpb\nbHBAVeWeucSh4our3s+SY4SNf4rviEt97mokGtKBn60LVbzH7CpSxDJlCFdSE/s/\n+MUwvIqzCk6k6pIyoF+LWZ0CgYBosLgP/hF4CiQUbwvVCH6rjNzkfsqwWXPJkk2e\nxWpgFHi0ic/e904P1DPxFGnb84iZsOwWkefple80ef2rrU9Sc+ogVI5GlQNNDKzt\n+wmcbvFfP315Ul+Vn7q/bh65Ncj3rADgRPQS6As/+Afltj/hCOcDl7HNUjoSS3/u\nSkGQoQKBgQCNDLkWVeAQefC319GosByvCb9zzug1hXr6IKguWJvNOC+I/TPwRdEz\nQby3s5IL3rf1ZfJfOWlFXYeV41OQOOnsvpE1+ORJUjpJ7RgQsFWewLsSitRpxekU\nEV8b1+7OWOeTgu9M65XtZapKXb9pFE+OCit+q4fbAGcAh8ITt66aJw==\n-----END RSA PRIVATE KEY-----\n","appId":"100962076121426673","clientId":"100962076121492209@zitadel_net"} \ No newline at end of file diff --git a/tests/Zitadel.Test/service-account.json b/tests/Zitadel.Test/service-account.json new file mode 100644 index 00000000..b6e85660 --- /dev/null +++ b/tests/Zitadel.Test/service-account.json @@ -0,0 +1,6 @@ +{ + "type": "serviceaccount", + "keyId": "100820491081673449", + "key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAtxkh+++/afNlrpVQxCtcfwDtWtc4MwF/SfW3TNuXgnNP52MG\ni3Gb4voLJHh+IjuLubDt2GSkFsvRmdqNU8rYF5ATtVnSwaA8I76FZwHpTYPRqM4j\nrUjCQTv8V+1xKlzDANn0pq+aJuUwP89TmOgk+S5yKHdc9Am27SxFxXlIjeyr/t//\nQE4BLp8BLjbWqb04s9kWUdZhnPyvrjekmO2F0P58xUKq7Of7wgbA7aSY610TSzMj\nyZpToByO3NSaLOIQEmgiXMGXIrH1SeGvZ2YWe2ONoEp1RJBel2MvcfzOWIHWtsfG\nS8FCnq/tS4kTQaNvcSgv1OHHIaST11rZ7ETzNwIDAQABAoIBAQCNAmYRyMn5puGg\nWo4hBePuS6CAXU+CuJx3MS1Bm7xLUtMuuCzKJ2yOmVMLe2BorMMm9wO1q4xTohSC\nELbW2VkijGshN1QAmo62WHwUoHw0nlhRULBZO7ee1bu5W7vTjC0B8tlfpc1qNwe5\nnMoc8GvJG+P/Fi4YI4MllPsG4ijmYxfJKQcw5EZofW53RGdYsxBeAn6AHeK1To9/\nMVidEEQFHpDP4S5sE+oE8PAmy4FXpTUkCHqK0hj9Iyn1MvqzYBA9AkPJHkDOgMIG\nu2yH4ld1+BqPOFaD2i9+n++DS7PuD0k5GAZnHVNvOef7p0zzcf7pU8HvQ81rY+en\nP5yj58BBAoGBAMPIajJkhJOqCVSTEK+OobNeq/htdGk0A0soj1X11Oj8OdQqcmti\nRsAib4AGsuZ232dCQktHrRgehvbOX11KawvNm6IHVV55v8BdXaFNPknUszduMUH/\nRZKzrIJbvHmJh+A1NLMS5gGJ27pLuOBEuQMuqjlgbz/wnFJgy1UyNjtrAoGBAO9p\n8rwhj8WBUawtK8JomOXw5WT4Pj+Q2tq78fSQd4RBN5+lJoX/PHMMFB+hYG5Pc5Wr\ntQB/6jXer/k8h0EnT1rLLVBaxMAfTRivVrbN6sZmlFX/CbYcLyWFF0yefg2Bf6ro\ny8N+JJq7vENIcxzRqoCSakQvWzaRuGznsWFoMQZlAoGBALQa7V7YS4IP/S9ZbxMh\nUi3RHMCX6GxTVehRMrLwSGHgjk5YkhOpP87Gnu4Wu4L5vgCe2p+oG1tB0gS/ysjm\nid080zxWJLmZpMWQ3m5hwbhnvEp0y0mcTONs+HifbZ4c3+to5scJzp+Bs6oIuNVP\nQyfjSOMpjI40iYna05LXcz5rAoGBAK1kJhBqnh2EkwoHjPG3n0SpOvntb+fJmTdh\ndtKQ7n4yXX6ZtVTGjZkk3k4qKhS6JhP3z5cVDSMGrJvtYmeleKthCuhvVJ9wC/4c\nvsiWSbp3AGdn1ji2IM8jnMQ45UISz8uP3kMvUINgA/EWv4fXsYx8fcn0IjNbRh5Z\njvXk59yhAoGAWvPOyX+g02Oi4958WpOyG3hU5XxPUxWB1FMTsusatARwXOwbbl92\n5bBgz7eb4wXhfUf7U3sItIH4Kf7xSrZwwq2cbTfBUE7k78kPhYOaJQD7LxxmdE98\n44Kc2VWK82jZIhgcswjRuuxpTMzWB89mC5UQtzykK5/m7+LLbOMdtHg=\n-----END RSA PRIVATE KEY-----\n", + "userId": "85328809577999392" +}