// Copyright 2018 Istio 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";

import "gogoproto/gogo.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/duration.proto";
import "policy/v1beta1/value_type.proto";

package istio.policy.v1beta1;

option go_package="istio.io/api/policy/v1beta1";

// AttributeManifest describes a set of Attributes produced by some component
// of an Istio deployment.
message AttributeManifest {
  // Optional. The revision of this document. Assigned by server.
  string revision = 1;
  // Required. Name of the component producing these attributes. This can be
  // the proxy (with the canonical name `istio-proxy`) or the name of an
  // `attributes` kind adapter in Mixer.
  string name = 2;

  // AttributeInfo describes the schema of an Istio `Attribute`.
  //
  // # Istio Attributes
  //
  // Istio uses `attributes` to describe runtime activities of Istio services.
  // An Istio attribute carries a specific piece of information about an activity,
  // such as the error code of an API request, the latency of an API request, or the
  // original IP address of a TCP connection. The attributes are often generated
  // and consumed by different services. For example, a frontend service can
  // generate an authenticated user attribute and pass it to a backend service for
  // access control purpose.
  //
  // To simplify the system and improve developer experience, Istio uses
  // shared attribute definitions across all components. For example, the same
  // authenticated user attribute will be used for logging, monitoring, analytics,
  // billing, access control, auditing. Many Istio components provide their
  // functionality by collecting, generating, and operating on attributes.
  // For example, the proxy collects the error code attribute, and the logging
  // stores it into a log.
  //
  // # Design
  //
  // Each Istio attribute must conform to an `AttributeInfo` in an
  // `AttributeManifest` in the current Istio deployment at runtime. An
  // [`AttributeInfo`][istio.policy.v1beta1] is used to define an attribute's
  // metadata: the type of its value and a detailed description that explains
  // the semantics of the attribute type. Each attribute's name is globally unique;
  // in other words an attribute name can only appear once across all manifests.
  //
  // The runtime presentation of an attribute is intentionally left out of this
  // specification, because passing attribute using JSON, XML, or Protocol Buffers
  // does not change the semantics of the attribute. Different implementations
  // can choose different representations based on their needs.
  //
  // # HTTP Mapping
  //
  // Because many systems already have REST APIs, it makes sense to define a
  // standard HTTP mapping for Istio attributes that are compatible with typical
  // REST APIs. The design is to map one attribute to one HTTP header, the
  // attribute name and value becomes the HTTP header name and value. The actual
  // encoding scheme will be decided later.
  message AttributeInfo {
    // Optional. A human-readable description of the attribute's purpose.
    string description = 1;

    // Required. The type of data carried by this attribute.
    istio.policy.v1beta1.ValueType value_type = 2;
  }
  // The set of attributes this Istio component will be responsible for producing at runtime.
  // We map from attribute name to the attribute's specification. The name of an attribute,
  // which is how attributes are referred to in aspect configuration, must conform to:
  //
  //     Name = IDENT { SEPARATOR IDENT };
  //
  // Where `IDENT` must match the regular expression `[a-z][a-z0-9]+` and `SEPARATOR` must
  // match the regular expression `[\.-]`.
  //
  // Attribute names must be unique within a single Istio deployment. The set of canonical
  // attributes are described at [here](https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/).
  // Attributes not in that list should be named with a component-specific suffix such as
  // `request.count-my.component`.
  map<string, AttributeInfo> attributes = 3;
}

// A Rule is a selector and a set of intentions to be executed when the
// selector is `true`
//
// The following example instructs Mixer to invoke `prometheus-handler` handler for all services and pass it the
// instance constructed using the 'RequestCountByService' instance.
//
// ```yaml
// - match: match(destination.service.host, "*")
//   actions:
//   - handler: prometheus-handler
//     instances:
//     - RequestCountByService
// ```
message Rule {
  // Required. Match is an attribute based predicate. When Mixer receives a
  // request it evaluates the match expression and executes all the associated `actions`
  // if the match evaluates to true.
  //
  // A few example match:
  //
  // * an empty match evaluates to `true`
  // * `true`, a boolean literal; a rule with this match will always be executed
  // * `match(destination.service.host, "ratings.*")` selects any request targeting a service whose
  // name starts with "ratings"
  // * `attr1 == "20" && attr2 == "30"` logical AND, OR, and NOT are also available
  string match = 1;

  // Optional. The actions that will be executed when match evaluates to `true`.
  repeated Action actions = 2;

  // A template for an HTTP header manipulation. Values in the template are expressions
  // that may reference action outputs by name. For example, if an action `x` produces an output
  // with a field `f`, then the header value expressions may use attribute `x.output.f` to reference
  // the field value:
  //
  // ```yaml
  // request_header_operations:
  // - name: x-istio-header
  //   values:
  //   - x.output.f
  // ```
  //
  // If the header value expression evaluates to an empty string, and the operation is to either replace
  // or append a header, then the operation is not applied. This permits conditional behavior on behalf of the
  // adapter to optionally modify the headers.
  message HeaderOperationTemplate {

    // Required. Header name literal value.
    string name = 1;

    // Optional. Header value expressions.
    repeated string values = 2;

    // Header operation type.
    enum Operation {
      // Replace a header by name.
      REPLACE = 0;

      // Remove a header by name. Values are ignored.
      REMOVE = 1;

      // Append values to the existing header values.
      APPEND = 2;
    }

    // Optional. Header operation type. Default operation is to replace the value of the header by name.
    Operation operation = 3;
  }

  // Optional. Templatized operations on the request headers using values produced by the
  // rule actions. Require the check action result to be OK.
  repeated HeaderOperationTemplate request_header_operations = 3;

  // Optional. Templatized operations on the response headers using values produced by the
  // rule actions. Require the check action result to be OK.
  repeated HeaderOperationTemplate response_header_operations = 4;

  // $hide_from_docs
  // Optional. Provides the ability to add a sampling configuration for Mixer rules. This sampling
  // will limit the scenarios in which the `actions` of the rule are executed. The sampling will
  // only take place after a `match` predicate has evaluated to true.
  //
  // Default behavior is no sampling (the `actions` are executed for all requests).
  Sampling sampling = 5;
}

// Action describes which [Handler][istio.policy.v1beta1.Handler] to invoke and what data to pass to it for processing.
//
// The following example instructs Mixer to invoke 'prometheus-handler' handler and pass it the object
// constructed using the instance 'RequestCountByService'.
//
// ```yaml
//   handler: prometheus-handler
//   instances:
//   - RequestCountByService
// ```
message Action {
  // Required. Fully qualified name of the handler to invoke.
  // Must match the `name` of a [Handler][istio.policy.v1beta1.Handler.name].
  string handler = 2;

  // Required. Each value must match the fully qualified name of the
  // [Instance][istio.policy.v1beta1.Instance.name]s.
  // Referenced instances are evaluated by resolving the attributes/literals for all the fields.
  // The constructed objects are then passed to the `handler` referenced within this action.
  repeated string instances = 3;

  // Optional. A handle to refer to the results of the action.
  string name = 4;
}

// An Instance tells Mixer how to create instances for particular template.
//
// Instance is defined by the operator. Instance is defined relative to a known
// template. Their purpose is to tell Mixer how to use attributes or literals to produce
// instances of the specified template at runtime.
//
// The following example instructs Mixer to construct an instance associated with template
// 'istio.mixer.adapter.metric.Metric'. It provides a mapping from the template's fields to expressions.
// Instances produced with this instance can be referenced by [Actions][istio.policy.v1beta1.Action] using name
// 'RequestCountByService'
//
// ```yaml
// - name: RequestCountByService
//   template: istio.mixer.adapter.metric.Metric
//   params:
//     value: 1
//     dimensions:
//       source: source.name
//       destination_ip: destination.ip
// ```
message Instance {
  // Required. The name of this instance
  //
  // Must be unique amongst other Instances in scope. Used by [Action][istio.policy.v1beta1.Action] to refer
  // to an instance produced by this instance.
  string name = 1;

  // Required. The name of the compiled in template this instance creates instances for. For referencing non compiled-in
  // templates, use the `template` field instead.
  //
  // The value must match the name of the available template Mixer is built with.
  string compiled_template = 67794676;

  // Required. The name of the template this instance creates instances for. For referencing compiled-in
  // templates, use the `compiled_template` field instead.
  //
  // The value must match the name of the available template in scope.
  string template = 2;

  // Required. Depends on referenced template. Struct representation of a
  // proto defined by the template; this varies depending on the value of field `template`.
  google.protobuf.Struct params = 3;

  // Optional. Defines attribute bindings to map the output of attribute-producing adapters back into
  // the attribute space. The variable `output` refers to the output template instance produced
  // by the adapter.
  // The following example derives `source.namespace` from `source.uid` in the context of Kubernetes:
  // ```yaml
  // params:
  //   # Pass the required attribute data to the adapter
  //   source_uid: source.uid | ""
  // attribute_bindings:
  //   # Fill the new attributes from the adapter produced output
  //   source.namespace: output.source_namespace
  // ```
  map<string, string> attribute_bindings = 4;
}

// Handler allows the operator to configure a specific adapter implementation.
// Each adapter implementation defines its own `params` proto.
//
// In the following example we define a `metrics` handler for the `prometheus` adapter.
// The example is in the form of a Kubernetes resource:
// * The `metadata.name` is the name of the handler
// * The `kind` refers to the adapter name
// * The `spec` block represents adapter-specific configuration as well as the connection information
//
// ```yaml
// # Sample-1: No connection specified (for compiled in adapters)
// # Note: if connection information is not specified, the adapter configuration is directly inside
// # `spec` block. This is going to be DEPRECATED in favor of Sample-2
// apiVersion: "config.istio.io/v1alpha2"
// kind: prometheus
// metadata:
//   name: handler
//   namespace: istio-system
// spec:
//   metrics:
//   - name: request_count
//     instance_name: requestcount.metric.istio-system
//     kind: COUNTER
//     label_names:
//     - source_service
//     - source_version
//     - destination_service
//     - destination_version
// ---
// # Sample-2: With connection information (for out-of-process adapters)
// # Note: Unlike sample-1, the adapter configuration is parallel to `connection` and is nested inside `param` block.
// apiVersion: "config.istio.io/v1alpha2"
// kind: prometheus
// metadata:
//   name: handler
//   namespace: istio-system
// spec:
//   param:
//     metrics:
//     - name: request_count
//       instance_name: requestcount.metric.istio-system
//       kind: COUNTER
//       label_names:
//       - source_service
//       - source_version
//       - destination_service
//       - destination_version
//   connection:
//     address: localhost:8090
// ---
// ```
message Handler {
  // Required. Must be unique in the entire Mixer configuration. Used by [Actions][istio.policy.v1beta1.Action.handler]
  // to refer to this handler.
  string name = 1;

  // Required. The name of the compiled in adapter this handler instantiates. For referencing non compiled-in
  // adapters, use the `adapter` field instead.
  //
  // The value must match the name of the available adapter Mixer is built with. An adapter's name is typically a
  // constant in its code.
  string compiled_adapter = 67794676;

  // Required. The name of a specific adapter implementation. For referencing compiled-in
  // adapters, use the `compiled_adapter` field instead.
  //
  // An adapter's implementation name is typically a constant in its code.
  string adapter = 2;

  // Optional. Depends on adapter implementation. Struct representation of a
  // proto defined by the adapter implementation; this varies depending on the value of field `adapter`.
  google.protobuf.Struct params = 3;

  // Optional. Information on how to connect to the out-of-process adapter.
  // This is used if the adapter is not compiled into Mixer binary and is running as a separate process.
  Connection connection = 4;
}

// Connection allows the operator to specify the endpoint for out-of-process infrastructure backend.
// Connection is part of the handler custom resource and is specified alongside adapter specific configuration.
message Connection {
  // The address of the backend.
  string address = 2;

  // Timeout for remote calls to the backend.
  google.protobuf.Duration timeout = 3 [(gogoproto.stdduration) = true];

  // Auth config for the connection to the backend. If omitted, plain text will
  // be used.
  Authentication authentication = 4;
}

// $hide_from_docs
// Sampling provides configuration of sampling strategies for Rule actions.
// Multiple sampling strategies are supported. When multiple strategies are configured,
// a request must be selected by all configured sampling strategies.
message Sampling {
  // Optional. Provides filtering of actions based on random selection per request.
  RandomSampling random = 1;
  // Optional. Provides filtering of actions based on number of requests observed within
  // a configured time window.
  RateLimitSampling rate_limit = 2;
}

// $hide_from_docs
// RandomSampling will filter based on the comparison of a randomly-generated value
// against the threshold provided.
//
// Example: To restrict the execution of Rule actions to only 12.5% of requests, the
// `sampling_rate` would be set `12.5`.
//
// This sampling configuration is meant to closely match the access log RuntimeFilter configuration
// [supported by Envoy](https://github.com/envoyproxy/data-plane-api/blob/master/envoy/config/filter/accesslog/v2/accesslog.proto#L113)
message RandomSampling {
  // Specifies an attribute expression to use to override the numerator in the `percent_sampled` field.
  // If this value is set, but no value is found OR if that value is not a numeric value, then
  // the derived sampling rate will be 0 (meaning no `Action`s are executed for a `Rule`).
  string attribute_expression = 1;
  // The default sampling rate, expressed as a percentage. Defaults to 0% with a denominator
  // of 100.
  FractionalPercent percent_sampled = 2;
  // By default sampling will be based on the value of the request header `x-request-id`.
  // This behavior will cause consistent sampling across `Rule`s and for the full trace of a
  // request through a mesh (across hosts). If that value is not present and/or
  // `use_independent_randomness` is set to true, the sampling will be done based on the value of
  // attribute specified in `attribute_epxression`. If that attribute does not exist, the system
  // will behave as if the sampling rate was 0 (meaning no `Action`s are executed for a `Rule`).
  bool use_independent_randomness = 3;
}

// $hide_from_docs
// RateLimitSampling provides the ability to limit the number of Rule action executions that
// occur over a period of time.
message RateLimitSampling {
  // Window in which to enforce the sampling rate.
  google.protobuf.Duration sampling_duration = 1 [(gogoproto.nullable)=false,(gogoproto.stdduration) = true];
  // Number of entries to allow during the `sampling_duration` before sampling is enforced.
  int64 max_unsampled_entries = 2;
  // The rate at which to sample entries once the unsampled limit has been reached. Sampling will be enforced
  // as 1 per every `sampling_rate` entries allowed.
  int64 sampling_rate = 3;
}

// $hide_from_docs
// A fractional percentage is used in cases in which for performance reasons performing floating
// point to integer conversions during randomness calculations is undesirable. The message includes
// both a numerator and denominator that together determine the final fractional value.
//
// * **Example**: 1/100 = 1%.
// * **Example**: 3/10000 = 0.03%.
message FractionalPercent {
  // Specifies the numerator. Defaults to 0.
  uint32 numerator = 1;

  // Fraction percentages support several fixed denominator values.
  enum DenominatorType {
    // 100.
    //
    // **Example**: 1/100 = 1%.
    HUNDRED = 0;

    // 10,000.
    //
    // **Example**: 1/10000 = 0.01%.
    TEN_THOUSAND = 1;
  }

  // Specifies the denominator. If the denominator specified is less than the numerator, the final
  // fractional percentage is capped at 1 (100%).
  DenominatorType denominator = 2;
}

// Authentication allows the operator to specify the authentication of
// connections to out-of-process infrastructure backend.
message Authentication {
  oneof auth_type {
    // Originate a TLS connection to the adapter and present an auth token
    // in each call for client authentication.
    Tls tls = 1;

    // Secure connections to the adapter using mutual TLS by presenting
    // client certificates for authentication.
    Mutual mutual = 2;
  };
}

// Tls let operator specify client authentication setting when TLS is used for
// connection to the backend.
message Tls {
  // The path to the file holding additional CA certificates to well known
  // public certs.
  string ca_certificates = 1;

  // Specifies how to get access token for client authn and authz.
  oneof token_source {
    // The path to the file holding the auth token (password, jwt token, api
    // key, etc).
    string token_path = 2;

    // Oauth config to fetch access token from auth provider.
    OAuth oauth = 3;
  }

  // AuthHeader specifies how to pass access token with authorization header.
  enum AuthHeader {
    // Access token is passed in authorization header as what it is
    // (authorization: some-token).
    PLAIN = 0;

    // Access token is passed to adapter as bearer token (i.e. authorization:
    // bearer some-token).
    BEARER = 1;
  }

  // Specifies how to pass access token to the adapter backend.
  oneof token_type {
    // Access token is passed as authorization header.
    AuthHeader auth_header = 4;

    // Customized header key to hold access token, e.g. x-api-key. Token will be
    // passed as what it is.
    string custom_header = 5;
  }

  // Used to configure mixer TLS client to verify the hostname on the returned
  // certificates. It is also included in the client's handshake to support SNI.
  string server_name = 6;
}

// OAuth let operator specify config to fetch access token via oauth when using
// TLS for connection to the backend.
message OAuth {
  // REQUIRED. OAuth client id for mixer.
  string client_id = 1;

  // REQUIRED. The path to the file holding the client secret for oauth.
  string client_secret = 2;

  // REQUIRED. The Resource server's token endpoint URL.
  string token_url = 3;

  // List of requested permissions.
  repeated string scopes = 4;

  // Additional parameters for requests to the token endpoint.
  map<string, string> endpoint_params = 5;
}

// Mutual let operator specify TLS configuration for Mixer as client if mutual TLS is used to
// secure connection to adapter backend.
message Mutual {
  // The path to the file holding the private key for mutual TLS. If omitted, the
  // default Mixer private key will be used.
  string private_key = 1;

  // The path to the file holding client certificate for mutual TLS. If omitted, the
  // default Mixer certificates will be used.
  string client_certificate = 2;

  // The path to the file holding additional CA certificates that are needed to
  // verify the presented adapter certificates. By default Mixer should already
  // include Istio CA certificates and system certificates in cert pool.
  string ca_certificates = 3;

  // Used to configure mixer mutual TLS client to supply server name for SNI.
  // It is not used to verify the hostname of the peer certificate, since
  // Istio verifies whitelisted SAN fields in mutual TLS.
  string server_name = 4;
}
