syntax = "proto3";

// If you make any changes make sure you run: make regenerate-proto

package gitlab.agent.gitlab.api;

option go_package = "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/gitlab/api";

import "pkg/agentcfg/agentcfg.proto";
import "pkg/constraint/constraint.proto";
import "buf/validate/validate.proto";
import "pkg/entity/entity.proto";

// Configuration contains shared fields from agentcfg.CiAccessProjectCF and agentcfg.CiAccessGroupCF.
// It is used to parse response from the allowed_agents API endpoint.
// See https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/kubernetes_ci_access.md#apiv4joballowed_agents-api.
message Configuration {
  string default_namespace = 1 [json_name = "default_namespace"];
  agentcfg.CiAccessAsCF access_as = 2 [json_name = "access_as"];
}

message AllowedAgent {
  int64 id = 1 [json_name = "id"];
  ConfigProject config_project = 2 [json_name = "config_project", (buf.validate.field).required = true];
  Configuration configuration = 3 [json_name = "configuration"];
}

message ConfigProject {
  int64 id = 1 [json_name = "id"];
}

message Pipeline {
  int64 id = 1 [json_name = "id"];
}

message Project {
  int64 id = 1 [json_name = "id"];
  repeated Group groups = 2 [json_name = "groups"];
}

message Group {
  int64 id = 1 [json_name = "id"];
}

message Job {
  int64 id = 1 [json_name = "id"];
}

message User {
  int64 id = 1 [json_name = "id"];
  string username = 2 [json_name = "username", (buf.validate.field).string.min_bytes = 1];
}

message Environment {
  string slug = 1 [json_name = "slug", (buf.validate.field).string.min_bytes = 1];
  string tier = 2 [json_name = "tier", (buf.validate.field).string.min_bytes = 1];
}

message GetAgentkInfoResponse {
  int64 project_id = 1 [json_name = "project_id", (buf.validate.field).int64.gt = 0];
  int64 agent_id = 2 [json_name = "agent_id", (buf.validate.field).int64.gt = 0];
  string agent_name = 3 [json_name = "agent_name", (buf.validate.field).string.(constraint.is_agent_name) = true];
  entity.GitalyInfo gitaly_info = 4 [json_name = "gitaly_info", (buf.validate.field).required = true];
  entity.GitalyRepository gitaly_repository = 5 [json_name = "gitaly_repository", (buf.validate.field).required = true];
  string default_branch = 6 [json_name = "default_branch", (buf.validate.field).string.min_bytes = 1];
}

message GetAgentwInfoResponse {
  int64 workspace_id = 1 [json_name = "workspace_id", (buf.validate.field).int64.gt = 0];
}

message GetRunnerControllerInfoResponse {
  int64 agent_id = 1 [json_name = "agent_id", (buf.validate.field).int64.gt = 0];
}

message GetRunnerInfoResponse {
  int64 runner_id = 1 [json_name = "runner_id", (buf.validate.field).int64.gt = 0];
}

message GetRepositoryInfoResponse {
  int64 project_id = 1 [json_name = "project_id", (buf.validate.field).int64.gt = 0];
  entity.GitalyInfo gitaly_info = 2 [json_name = "gitaly_info", (buf.validate.field).required = true];
  entity.GitalyRepository gitaly_repository = 3 [json_name = "gitaly_repository", (buf.validate.field).required = true];
  string default_branch = 4 [json_name = "default_branch", (buf.validate.field).string.min_bytes = 1];
}

message AllowedAgentsForJobAPIResponse {
  repeated AllowedAgent allowed_agents = 1 [json_name = "allowed_agents"];
  Job job = 2 [json_name = "job", (buf.validate.field).required = true];
  Pipeline pipeline = 3 [json_name = "pipeline", (buf.validate.field).required = true];
  Project project = 4 [json_name = "project", (buf.validate.field).required = true];
  User user = 5 [json_name = "user", (buf.validate.field).required = true];
  Environment environment = 6 [json_name = "environment"]; // optional
}

message AuthorizeProxyUserAPIRequest {
  int64 agent_id = 1 [json_name = "agent_id", (buf.validate.field).int64.gt = 0];
  string access_type = 2 [json_name = "access_type", (buf.validate.field).string = {in: ["session_cookie", "personal_access_token"]}];
  string access_key = 3 [json_name = "access_key", (buf.validate.field).string.min_bytes = 1];
  string csrf_token = 4 [json_name = "csrf_token"];
}

message AuthorizeProxyUserAPIResponse {
  AuthorizedAgentForUser agent = 1 [json_name = "agent", (buf.validate.field).required = true];
  User user = 2 [json_name = "user", (buf.validate.field).required = true];
  AccessAsProxyAuthorization access_as = 3 [json_name = "access_as", (buf.validate.field).required = true];
}

message AuthorizedAgentForUser {
  int64 id = 1 [json_name = "id"];
  ConfigProject config_project = 2 [json_name = "config_project", (buf.validate.field).required = true];
}

message AccessAsProxyAuthorization {
  oneof access_as {
    option (buf.validate.oneof).required = true;
    AccessAsAgentAuthorization agent = 1 [json_name = "agent"];
    AccessAsUserAuthorization user = 2 [json_name = "user"];
  }
}

message AccessAsAgentAuthorization {}

message AccessAsUserAuthorization {
  repeated ProjectAccessCF projects = 1 [json_name = "projects"];
  repeated GroupAccessCF groups = 2 [json_name = "groups"];
}

message ProjectAccessCF {
  int64 id = 1 [json_name = "id"];
  repeated string roles = 2 [json_name = "roles"];
}

message GroupAccessCF {
  int64 id = 1 [json_name = "id"];
  repeated string roles = 2 [json_name = "roles"];
}

message AgentConfigurationRequest {
  int64 agent_id = 1 [json_name = "agent_id", (buf.validate.field).int64.gt = 0];
  agentcfg.ConfigurationFile agent_config = 2 [json_name = "agent_config", (buf.validate.field).required = true];
  string agent_config_commit_id = 3 [json_name = "agent_config_commit_id"]; // empty when ref/repo was not found.
}

message ReceptiveAgent {
  // Agent ID.
  int64 id = 1 [json_name = "id", (buf.validate.field).int64.gt = 0];
  // URL of the receptive agent.
  string url = 2 [json_name = "url", (buf.validate.field).string.uri = true];
  // Certificate authority certificate to validate the receptive agent's TLS certificate. Optional.
  string ca_cert = 3 [json_name = "ca_cert"];
  // Host name to use for TLS certificate validation instead of the one in the URL. Optional.
  string tls_host = 4 [json_name = "tls_host"];

  oneof auth_config {
    option (buf.validate.oneof).required = true;

    // Configuration for Private Key based authentication to the agent.
    ReceptiveAgentJWTAuth jwt = 5 [json_name = "jwt"];

    // Configuration for mutual TLS based authentication to the agent.
    ReceptiveAgentMutualTLSAuth mtls = 6 [json_name = "mtls"];
  }
}

message ReceptiveAgentJWTAuth {
  // base64-encoded EdDSA private key to generate a JWT signature for each request.
  string private_key = 1 [json_name = "private_key", (buf.validate.field).string.min_bytes = 1];
}

message ReceptiveAgentMutualTLSAuth {
  // Client certificate for mTLS. Optional.
  string client_cert = 1 [json_name = "client_cert", (buf.validate.field).string.min_bytes = 1];
  // Client key for mTLS. Optional.
  string client_key = 2 [json_name = "client_key", (buf.validate.field).string.min_bytes = 1];
}

message GetReceptiveAgentsResponse {
  repeated ReceptiveAgent agents = 1 [json_name = "agents"];
}

message GetWorkspacesServerConfigResponse {
  string oauth_client_id = 1 [json_name = "oauth_client_id", (buf.validate.field).string.min_bytes = 1];
  string oauth_redirect_url = 2 [json_name = "oauth_redirect_url", (buf.validate.field).string.min_bytes = 1];
  string api_external_url = 3 [json_name = "api_external_url", (buf.validate.field).string.min_bytes = 1];
}

message GetOpenIDConfigurationResponse {
  string issuer = 1 [json_name = "issuer"];
  string authorization_endpoint = 2 [json_name = "authorization_endpoint"];
  string token_endpoint = 3 [json_name = "token_endpoint"];
  string device_authorization_endpoint = 4 [json_name = "device_authorization_endpoint"];
  string userinfo_endpoint = 5 [json_name = "userinfo_endpoint"];
  string jwks_uri = 6 [json_name = "jwks_uri"];
  repeated string id_token_signing_alg_values_supported = 7 [json_name = "id_token_signing_alg_values_supported"];
}

enum AuthorizeUserWorkspaceAccessStatus {
  INVALID_HOST = 0;
  NOT_AUTHORIZED = 1;
  AUTHORIZED = 2;
  WORKSPACE_NOT_FOUND = 3;
  PORT_NOT_FOUND = 4;
};

message AuthorizeUserWorkspaceAccessInfo {
  int64 id = 1 [json_name = "workspace_id"];
  // Maximum value for a network port is an unsigned 16-bit integer - 65535 .
  uint32 port = 2 [json_name = "workspace_port", (buf.validate.field).uint32.lte = 65535];
}

message AuthorizeUserWorkspaceAccessResponse {
  AuthorizeUserWorkspaceAccessStatus status = 1 [json_name = "status", (buf.validate.field).required = true];
  AuthorizeUserWorkspaceAccessInfo info = 2 [json_name = "info"];
}

message GetRunnerControllersForJobAdmissionAPIResponse {
  repeated RunnerController runner_controllers = 1 [json_name = "runner_controllers"];
}

message RunnerController {
  int64 id = 1 [json_name = "id", (buf.validate.field).int64.gt = 0];
  string state = 2 [
    json_name = "state",
    // NOTE: there is also a "disabled" state, but we don't expect it to be sent.
    // In the current implementation it must not be sent, because we just account for dry_run or non-dry_run state for simplicity.
    (buf.validate.field).string = {in: ["enabled", "dry_run"]}
  ];
}

// UpdateCIJobAPIRequest request message to update job in Rails
// See https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/api/ci/runner.rb#L259
message UpdateCIJobAPIRequest {
  string job_token = 1 [json_name = "token", (buf.validate.field).string.min_bytes = 1 ];
  string state = 3 [
    json_name = "state",
    (buf.validate.field).string.in = "failed"
  ];
  string failure_reason = 4 [
    json_name = "failure_reason",
    (buf.validate.field).string.in = "job_router_failure"
  ];
  string failure_message = 5 [json_name = "failure_message"];
}
