syntax = "proto3";
package spire.api.server.entry.v1;
option go_package = "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1;entryv1";

import "spire/api/types/entry.proto";
import "spire/api/types/federateswith.proto";
import "spire/api/types/selector.proto";
import "spire/api/types/spiffeid.proto";
import "spire/api/types/status.proto";
import "google/protobuf/wrappers.proto";

// Manages registration entries stored by the SPIRE Server.
service Entry {
    // Count entries.
    //
    // The caller must be local or present an admin X509-SVID.
    rpc CountEntries(CountEntriesRequest) returns (CountEntriesResponse);

    // Lists entries.
    //
    // The caller must be local or present an admin X509-SVID.
    rpc ListEntries(ListEntriesRequest) returns (ListEntriesResponse);

    // Gets an entry. If the entry does not exist, NOT_FOUND is returned.
    //
    // The caller must be local or present an admin X509-SVID.
    rpc GetEntry(GetEntryRequest) returns (spire.api.types.Entry);

    // Batch creates one or more entries.
    //
    // The caller must be local or present an admin X509-SVID.
    rpc BatchCreateEntry(BatchCreateEntryRequest) returns (BatchCreateEntryResponse);

    // Batch updates one or more entries.
    //
    // The caller must be local or present an admin X509-SVID.
    rpc BatchUpdateEntry(BatchUpdateEntryRequest) returns (BatchUpdateEntryResponse);

    // Batch deletes one or more entries.
    //
    // The caller must be local or present an admin X509-SVID.
    rpc BatchDeleteEntry(BatchDeleteEntryRequest) returns (BatchDeleteEntryResponse);

    // Gets the entries the caller is authorized for.
    //
    // The caller must present an active agent X509-SVID. See the Agent
    // AttestAgent/RenewAgent RPCs.
    rpc GetAuthorizedEntries(GetAuthorizedEntriesRequest) returns (GetAuthorizedEntriesResponse);

    // Syncs authorized entries down to the caller. The caller controls which
    // entries the server sends down full details for. The flow is as follows:
    // 1. Caller opens up sync stream
    // 2. Server determines authorized entries for caller:
    //    - If there are less entries than a server-determined page size, go to (5).
    //    - Otherwise, go to (3).
    // 3. Server pages entry revisions to the caller (contains the entry ID and
    //    revision number). The "more" flag set for all pages but the last so
    //    that the caller knows when the server is done.
    // 4. Client determines which entries are new or updated (based on revision
    //    number) and asks for them by sending a request with the IDs.
    // 5. Server pages down entries to the caller for each ID identified in (4)
    //    or every entry in (2) if the number of entries was less than the
    //    server-determined page size. The "more" flag set for all pages but
    //    the last so that the caller knows when the server is done.
    // 6. Steps (4) and (5) are repeated until the caller has synced down the
    //    details for all new/updated entries and closes the stream.
    rpc SyncAuthorizedEntries(stream SyncAuthorizedEntriesRequest) returns (stream SyncAuthorizedEntriesResponse);
}

message CountEntriesRequest {
    message Filter {
        spire.api.types.SPIFFEID by_spiffe_id = 1;
        spire.api.types.SPIFFEID by_parent_id = 2;
        spire.api.types.SelectorMatch by_selectors = 3;
        spire.api.types.FederatesWithMatch by_federates_with = 4;
        google.protobuf.StringValue by_hint = 5;
        google.protobuf.BoolValue by_downstream = 6;
    }

    // Filters the entries returned in the response.
    Filter filter = 1;
}

message CountEntriesResponse {
    int32 count = 1;
}

message ListEntriesRequest {
    message Filter {
        spire.api.types.SPIFFEID by_spiffe_id = 1;
        spire.api.types.SPIFFEID by_parent_id = 2;
        spire.api.types.SelectorMatch by_selectors = 3;
        spire.api.types.FederatesWithMatch by_federates_with = 4;
        google.protobuf.StringValue by_hint = 5;
        google.protobuf.BoolValue by_downstream = 6;
    }

    // Filters the entries returned in the response.
    Filter filter = 1;

    // An output mask indicating the entry fields set in the response.
    spire.api.types.EntryMask output_mask = 2;

    // The maximum number of results to return. The server may further
    // constrain this value, or if zero, choose its own.
    int32 page_size = 3;

    // The next_page_token value returned from a previous request, if any.
    string page_token = 4;
}

message ListEntriesResponse {
    // The list of entries.
    repeated spire.api.types.Entry entries = 1;

    // The page token for the next request. Empty if there are no more results.
    // This field should be checked by clients even when a page_size was not
    // requested, since the server may choose its own (see page_size).
    string next_page_token = 2;
}

message GetEntryRequest {
    // Required. ID of the entry to get.
    string id = 1;

    // An output mask indicating the entry fields set in the response.
    spire.api.types.EntryMask output_mask = 2;
}

message BatchCreateEntryRequest {
    // The entries to be created. If no entry ID is provided, one will be 
    // generated.
    repeated spire.api.types.Entry entries = 1;

    // An output mask indicating the entry fields set in the response.
    spire.api.types.EntryMask output_mask = 2;
}

message BatchCreateEntryResponse {
    message Result {
        // The status of creating the entry. If status code will be
        // ALREADY_EXISTS if a similar entry already exists. An entry is
        // similar if it has the same spiffe_id, parent_id, and selectors.
        spire.api.types.Status status = 1;

        // The entry that was created (.e.g status code is OK) or that already
        // exists (i.e. status code is ALREADY_EXISTS).
        //
        // If the status code is any other value, this field will not be set.
        spire.api.types.Entry entry = 2;
    }

    // Result for each entry in the request (order is maintained).
    repeated Result results = 1;
}

message BatchUpdateEntryRequest {
    // The entries to be updated.
    repeated spire.api.types.Entry entries = 1;

    // An input mask indicating what entry fields should be updated.
    spire.api.types.EntryMask input_mask = 2;

    // An output mask indicating what entry fields are set in the response.
    spire.api.types.EntryMask output_mask = 3;
}

message BatchUpdateEntryResponse {
    message Result {
        // The status of creating the entry.
        spire.api.types.Status status = 1;

        // The entry that was updated. If the status is OK, it will be the
        // entry that was updated. If the status is any other value, this field
        // will not be set.
        spire.api.types.Entry entry = 2;
    }

    // Result for each entry in the request (order is maintained).
    repeated Result results = 1;
}

message BatchDeleteEntryRequest {
    // IDs of the entries to delete.
    repeated string ids = 1;
}

message BatchDeleteEntryResponse {
    message Result {
        // The status of creating the entry.
        spire.api.types.Status status = 1;

        // The ID of the entry that was deleted.
        string id = 2;
    }

    // Result for each entry ID in the request (order is maintained).
    repeated Result results = 1;
}

message GetAuthorizedEntriesRequest {
    // An output mask indicating which fields are set in the response.
    spire.api.types.EntryMask output_mask = 1;
}

message GetAuthorizedEntriesResponse {
    // The authorized entries.
    repeated spire.api.types.Entry entries = 1;
}

message SyncAuthorizedEntriesRequest {
    // An output mask indicating which fields are set in the response.
    spire.api.types.EntryMask output_mask = 1;

    // IDs of the entries to fetch in full. Sent by the client in response to
    // a sparse entry.
    repeated string ids = 2;
}

message SyncAuthorizedEntriesResponse {
    // The revisions of the authorized entries. This field is set when the
    // authorized entry list meets or exceeds the server-determined page size.
    // Callers use it to determine which entries are new/updated that they then
    // request on the stream.
    // See SyncAuthorizedEntries for details.
    repeated EntryRevision entry_revisions = 1;
   
    // The authorized entries. This field is set either 1) on the initial
    // response if the number of authorized entries is less than the page size
    // or 2) in response to the caller requesting the entries after determining
    // they need to details based on entry revisions provided in a previous
    // response.
    // See SyncAuthorizedEntries for details.
    repeated spire.api.types.Entry entries = 2;
   
    // Whether there are more entries to sync down in this response phase.
    bool more = 3;
}

message EntryRevision {
    // The entry ID.
    string id = 1;

    // The entry revision number.
    int64 revision_number = 2;

    // When the entry was created (seconds since Unix epoch).
    int64 created_at = 3;
}
