| // Copyright 2023 The LUCI Authors. |
| // |
| // 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 luci.sidecar; |
| |
| option go_package = "go.chromium.org/luci/common/proto/sidecar"; |
| |
| import "google/rpc/status.proto"; |
| |
| |
| // Auth exposes methods to authenticate user credentials and to make |
| // authorization checks. |
| service Auth { |
| // Authenticate receives metadata of the incoming call and uses it to |
| // authenticate the caller, i.e. it extracts appropriate credentials and |
| // verifies they are valid. |
| // |
| // Optionally checks if the authenticated identity is a member of groups |
| // given by `groups` request field, returning groups the identity is a member |
| // of in `groups` response field (which will be a subset of groups passed in |
| // the request). This is useful for implementing simple broad group-based |
| // authorization checks skipping extra RPCs. For more flexible checks see |
| // IsMember and HasPermission RPCs. |
| // |
| // Returns: |
| // * OK if the server understood the request and performed the |
| // authentication. The outcome (which can include an error if credentials |
| // are invalid) is available as part of AuthenticateResponse. OK is |
| // returned as well if the request doesn't have credentials attached at |
| // all or they were invalid. In that case AuthenticateResponse contains |
| // `anonymous` or `error` outcomes respectively. |
| // * UNAUTHENTICATED if the call to the sidecar server itself failed due to |
| // invalid (corrupted, expired, etc) RPC credentials, i.e. credentials of |
| // the sidecar client itself, not credentials inside AuthenticateRequest. |
| // This response MUST be presented as INTERNAL error to the end user, |
| // since it indicates some internal misconfiguration between the |
| // application server and the sidecar service, unrelated to credentials |
| // sent by the end-user. |
| // * PERMISSION_DENIED if the call to the sidecar server itself is not |
| // allowed. This response MUST also be presented as INTERNAL error to |
| // the end user. |
| // * INTERNAL on transient internal errors that SHOULD be retried. |
| rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse); |
| |
| // IsMember checks if an identity belongs to any of the given groups. |
| // |
| // Returns: |
| // * OK with the outcome of the check (which may be negative) if the check |
| // was performed successfully. |
| // * INVALID_ARGUMENT if the request is malformed. |
| // * UNAUTHENTICATED if the call to the sidecar server failed due to invalid |
| // (corrupted, expired, etc) RPC credentials. This response MUST be |
| // presented as INTERNAL error to the end user, since it indicates some |
| // internal misconfiguration between the application server and the |
| // sidecar service. |
| // * PERMISSION_DENIED if the call to the sidecar server itself is not |
| // allowed. This response MUST also be presented as INTERNAL error to |
| // the end user. |
| // * INTERNAL on transient internal errors that SHOULD be retried. |
| rpc IsMember(IsMemberRequest) returns (IsMemberResponse); |
| |
| // HasPermission check if an identity has a permission in a realm. |
| // |
| // Can only check permissions registered when the sidecar server was started |
| // via `-sidecar-subscribe-to-permission` command line flag. Checks for any |
| // other permission will end up with INVALID_ARGUMENT error. |
| // |
| // Returns: |
| // * OK with the outcome of the check (which may be negative) if the check |
| // was performed successfully. |
| // * INVALID_ARGUMENT if the request is malformed or the specified |
| // permission was not registered with the sidecar server via |
| // `-sidecar-subscribe-to-permission` command line flag. |
| // * UNAUTHENTICATED if the call to the sidecar server failed due to invalid |
| // (corrupted, expired, etc) RPC credentials. This response MUST be |
| // presented as INTERNAL error to the end user, since it indicates some |
| // internal misconfiguration between the application server and the |
| // sidecar service. |
| // * PERMISSION_DENIED if the call to the sidecar server itself is not |
| // allowed. This response MUST also be presented as INTERNAL error to |
| // the end user. |
| // * INTERNAL on transient internal errors that SHOULD be retried. |
| rpc HasPermission(HasPermissionRequest) returns (HasPermissionResponse); |
| } |
| |
| |
| // AuthenticateRequest contains information about an incoming request that needs |
| // to be authenticated. |
| // |
| // To be forward compatible the application server should send all incoming |
| // headers (or metadata in gRPC case) and let the sidecar server decide which |
| // entries to use. If necessary, the application server can omit entries that |
| // are obviously not used for authentication (for example custom metadata |
| // entries used by the application server itself). But generally it should not |
| // be cherry-picking headers it thinks carry authentication credentials and |
| // sending only them. |
| // |
| // Note that in environments where the application server runs behind a |
| // TLS-terminating load balancer (all cloud environments are like that), |
| // metadata with key `Host` (for HTTP v1) or `:authority` (for HTTP v2 and gRPC) |
| // is especially important to propagate, since it contains the verified |
| // (by the load balancer) hostname of the service being called. It is often |
| // needed to check JWT token audience. Omitting it may result in some JWT tokens |
| // not being authenticated. |
| // |
| // If the application server terminates TLS itself, it MUST also itself verify |
| // `Host` header (or `:authority` pseudo-header) matches the expected service |
| // hostname before calling Authenticate. |
| message AuthenticateRequest { |
| // The protocol used by the end user to call the application server. Affects |
| // how some metadata keys are interpreted. |
| enum Protocol { |
| PROTOCOL_UNSPECIFIED = 0; |
| HTTP1 = 1; |
| HTTP2 = 2; |
| GRPC = 3; |
| } |
| Protocol protocol = 1; |
| |
| // An HTTP header or gRPC metadatum. |
| message Metadata { |
| // Metadata key. Case-insensitive. |
| // |
| // If `protocol` is `GRPC`, keys ending with `-bin` indicate the value |
| // is base64-encoded. The application server MUST base64-encode binary |
| // metadata values before passing them to the sidecar server. |
| // |
| // For other protocols, keys ending with `-bin` have no special meaning, |
| // since they don't support arbitrary binary headers. |
| string key = 1; |
| // Metadata value. |
| // |
| // If `protocol` is `GRPC` and the key ends with `-bin`, this MUST be |
| // the base64-encoded value. The sidecar server will decode it into its |
| // original binary form before using it. |
| // |
| // For other protocols, keys ending with `-bin` have no special meaning, |
| // since they don't support arbitrary binary headers. |
| string value = 2; |
| } |
| repeated Metadata metadata = 2; |
| |
| // List of groups to check an authenticated identity is a member of. |
| // |
| // The result of this check is returned via `groups` response field. |
| repeated string groups = 3; |
| } |
| |
| |
| // AuthenticateResponse is a result of authentication (successful or not). |
| // |
| // The primary result of the authentication is `identity` which is a LUCI |
| // identity string (`<kind>:<value>` pair, e.g. `user:someone@example.com`). |
| // It can be passed to methods that do authorization checks. Additional details |
| // are available via `outcome` oneof. If the request is anonymous or |
| // authentication failed, the identity is set to `anonymous:anonymous`. |
| // |
| // If credentials are present, but invalid (e.g. expired JWT), error details are |
| // returned as part of `error` outcome. |
| message AuthenticateResponse { |
| // An authenticated identity (`<kind>:<value>`). Details are in `outcome`. |
| string identity = 1; |
| // Sidecar server information for logging and debugging. |
| ServerInfo server_info = 2; |
| |
| // List of groups the identity is a member of. |
| // |
| // This is a subset of groups passed via `groups` request field. |
| repeated string groups = 3; |
| |
| message Anonymous { |
| // Nothing here. |
| } |
| |
| message User { |
| // An authenticated user email. Always set. |
| string email = 1; |
| // A full user name, if available. |
| string name = 2; |
| // An URL to profile picture, if available. |
| string picture = 3; |
| // OAuth client ID if the request was authenticated using OAuth. |
| string client_id = 4; |
| } |
| |
| message Project { |
| // LUCI project name representing the context of the call. |
| string project = 1; |
| // Identity string of the LUCI service that makes the call. |
| string service = 2; |
| } |
| |
| oneof outcome { |
| // Set if the RPC to the sidecar succeeded, but passed credentials are bad. |
| google.rpc.Status error = 10; |
| // The request had no recognized credentials attached. |
| Anonymous anonymous = 11; |
| // The request had an end-user credentials attached. |
| User user = 12; |
| // The request is an internal LUCI call from another LUCI service. |
| Project project = 13; |
| } |
| } |
| |
| |
| // IsMemberRequest specifies an identity and a list of groups to check. |
| message IsMemberRequest { |
| // Identity to check a membership of as a `<kind>:<value>` string. |
| // |
| // This is the same identity as returned in AuthenticateResponse. Possible |
| // formats: |
| // * `anonymous:anonymous` for an anonymous caller. |
| // * `user:<email>` for an end user or a service account. |
| // * `project:<name>` for a LUCI project calling a LUCI service. |
| string identity = 1; |
| |
| // List of groups to check memberships in, must have at least one entry. |
| // |
| // The check is overall positive if `identity` is a member of at least one |
| // group here. |
| repeated string groups = 2; |
| } |
| |
| |
| // IsMemberResponse contains outcome of a groups membership check. |
| message IsMemberResponse { |
| // True if the identity is a member of at least one group. |
| bool is_member = 1; |
| // Sidecar server information for logging and debugging. |
| ServerInfo server_info = 2; |
| } |
| |
| |
| // HasPermissionRequest identifies an identity and a permission to check. |
| message HasPermissionRequest { |
| // Identity to check a permission of as a `<kind>:<value>` string. |
| // |
| // This is the same identity as returned in AuthenticateResponse. Possible |
| // formats: |
| // * `anonymous:anonymous` for an anonymous caller. |
| // * `user:<email>` for an end user or a service account. |
| // * `project:<name>` for a LUCI project calling a LUCI service. |
| string identity = 1; |
| |
| // Permission to check as `<service>.<subject>.<verb>` string. |
| // |
| // The sidecar server can only check permissions registered when it was |
| // started via `-sidecar-subscribe-to-permission` command line flag. Checks |
| // for any other permission will end up with INVALID_ARGUMENT error. |
| string permission = 2; |
| |
| // A realm to check the permission in as `<project>:<realm>` string. |
| // |
| // A non-existing realm is replaced with the corresponding root realm (e.g. if |
| // `projectA:some/realm` doesn't exist, `projectA:@root` will be used in its |
| // place). If the project doesn't exist, all its realms (including the root |
| // realm) are considered empty. The permission check ends with negative |
| // outcome in that case. |
| string realm = 3; |
| |
| // Attributes are the context of this particular permission check and are used |
| // as inputs to `conditions` predicates in conditional bindings. If a service |
| // supports conditional bindings, it must document what attributes it passes |
| // with each permission it checks. |
| map<string, string> attributes = 4; |
| } |
| |
| |
| // HasPermissionResponse contains outcome of a permission check. |
| message HasPermissionResponse { |
| // True if the identity has the requested permission. |
| bool has_permission = 1; |
| // Sidecar server information for logging and debugging. |
| ServerInfo server_info = 2; |
| } |
| |
| |
| // ServerInfo is returned with every response. It contains details about the |
| // sidecar server that handled the call and its current state. Useful for |
| // debugging. Should usually be logged by the application server in its internal |
| // logs. Do not return this to the end user. |
| message ServerInfo { |
| // Service name of the LUCI Sidecar server to identify its monitoring metrics. |
| string sidecar_service = 1; |
| // Job name of the LUCI Sidecar server to identify its monitoring metrics. |
| string sidecar_job = 2; |
| // Hostname of the LUCI Sidecar server to identify its monitoring metrics. |
| string sidecar_host = 3; |
| // Version of the LUCI Sidecar server for logs. |
| string sidecar_version = 4; |
| // Hostname of LUCI Auth service that produced AuthDB. |
| string auth_db_service = 5; |
| // Revision of LUCI AuthDB used during authorization checks. |
| int64 auth_db_rev = 6; |
| } |