From d07e5b5991243bd306b4ce9b03786b7a3e5532f9 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 12:10:39 +0200 Subject: [PATCH 01/13] change data model Signed-off-by: Miguel Martinez --- .../frontend/google/protobuf/descriptor.ts | 16 ++- app/controlplane/pkg/data/ent/apitoken.go | 34 ++++- .../pkg/data/ent/apitoken/apitoken.go | 31 +++++ .../pkg/data/ent/apitoken/where.go | 58 ++++++++ .../pkg/data/ent/apitoken_create.go | 97 +++++++++++++ .../pkg/data/ent/apitoken_query.go | 77 ++++++++++- .../pkg/data/ent/apitoken_update.go | 121 +++++++++++++++++ app/controlplane/pkg/data/ent/client.go | 16 +++ .../ent/migrate/migrations/20250626100818.sql | 8 ++ .../pkg/data/ent/migrate/migrations/atlas.sum | 3 +- .../pkg/data/ent/migrate/schema.go | 22 ++- app/controlplane/pkg/data/ent/mutation.go | 127 +++++++++++++++++- app/controlplane/pkg/data/ent/schema-viz.html | 2 +- .../pkg/data/ent/schema/apitoken.go | 14 +- 14 files changed, 606 insertions(+), 20 deletions(-) create mode 100644 app/controlplane/pkg/data/ent/migrate/migrations/20250626100818.sql diff --git a/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts b/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts index d59b21da4..0d2d2fb32 100644 --- a/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts +++ b/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts @@ -30,7 +30,7 @@ export enum Edition { EDITION_2024 = 1001, /** * EDITION_1_TEST_ONLY - Placeholder editions for testing feature resolution. These should not be - * used or relied on outside of tests. + * used or relyed on outside of tests. */ EDITION_1_TEST_ONLY = 1, EDITION_2_TEST_ONLY = 2, @@ -875,13 +875,12 @@ export interface MessageOptions { export interface FieldOptions { /** - * NOTE: ctype is deprecated. Use `features.(pb.cpp).string_type` instead. * The ctype option instructs the C++ code generator to use a different * representation of the field than it normally would. See the specific * options below. This option is only implemented to support use of * [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of - * type "bytes" in the open source release. - * TODO: make ctype actually deprecated. + * type "bytes" in the open source release -- sorry, we'll try to include + * other types in a future version! */ ctype: FieldOptions_CType; /** @@ -1053,7 +1052,11 @@ export function fieldOptions_JSTypeToJSON(object: FieldOptions_JSType): string { } } -/** If set to RETENTION_SOURCE, the option will be omitted from the binary. */ +/** + * If set to RETENTION_SOURCE, the option will be omitted from the binary. + * Note: as of January 2023, support for this is in progress and does not yet + * have an effect (b/264593489). + */ export enum FieldOptions_OptionRetention { RETENTION_UNKNOWN = 0, RETENTION_RUNTIME = 1, @@ -1096,7 +1099,8 @@ export function fieldOptions_OptionRetentionToJSON(object: FieldOptions_OptionRe /** * This indicates the types of entities that the field may apply to when used * as an option. If it is unset, then the field may be freely used as an - * option on any kind of entity. + * option on any kind of entity. Note: as of January 2023, support for this is + * in progress and does not yet have an effect (b/264593489). */ export enum FieldOptions_OptionTargetType { TARGET_TYPE_UNKNOWN = 0, diff --git a/app/controlplane/pkg/data/ent/apitoken.go b/app/controlplane/pkg/data/ent/apitoken.go index 8e583d58f..83fbf1c29 100644 --- a/app/controlplane/pkg/data/ent/apitoken.go +++ b/app/controlplane/pkg/data/ent/apitoken.go @@ -11,6 +11,7 @@ import ( "entgo.io/ent/dialect/sql" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" "github.com/google/uuid" ) @@ -31,6 +32,8 @@ type APIToken struct { RevokedAt time.Time `json:"revoked_at,omitempty"` // OrganizationID holds the value of the "organization_id" field. OrganizationID uuid.UUID `json:"organization_id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the APITokenQuery when eager-loading is set. Edges APITokenEdges `json:"edges"` @@ -41,9 +44,11 @@ type APIToken struct { type APITokenEdges struct { // Organization holds the value of the organization edge. Organization *Organization `json:"organization,omitempty"` + // Project holds the value of the project edge. + Project *Project `json:"project,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [1]bool + loadedTypes [2]bool } // OrganizationOrErr returns the Organization value or an error if the edge @@ -57,6 +62,17 @@ func (e APITokenEdges) OrganizationOrErr() (*Organization, error) { return nil, &NotLoadedError{edge: "organization"} } +// ProjectOrErr returns the Project value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e APITokenEdges) ProjectOrErr() (*Project, error) { + if e.Project != nil { + return e.Project, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: project.Label} + } + return nil, &NotLoadedError{edge: "project"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*APIToken) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -66,7 +82,7 @@ func (*APIToken) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullString) case apitoken.FieldCreatedAt, apitoken.FieldExpiresAt, apitoken.FieldRevokedAt: values[i] = new(sql.NullTime) - case apitoken.FieldID, apitoken.FieldOrganizationID: + case apitoken.FieldID, apitoken.FieldOrganizationID, apitoken.FieldProjectID: values[i] = new(uuid.UUID) default: values[i] = new(sql.UnknownType) @@ -125,6 +141,12 @@ func (at *APIToken) assignValues(columns []string, values []any) error { } else if value != nil { at.OrganizationID = *value } + case apitoken.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + at.ProjectID = *value + } default: at.selectValues.Set(columns[i], values[i]) } @@ -143,6 +165,11 @@ func (at *APIToken) QueryOrganization() *OrganizationQuery { return NewAPITokenClient(at.config).QueryOrganization(at) } +// QueryProject queries the "project" edge of the APIToken entity. +func (at *APIToken) QueryProject() *ProjectQuery { + return NewAPITokenClient(at.config).QueryProject(at) +} + // Update returns a builder for updating this APIToken. // Note that you need to call APIToken.Unwrap() before calling this method if this APIToken // was returned from a transaction, and the transaction was committed or rolled back. @@ -183,6 +210,9 @@ func (at *APIToken) String() string { builder.WriteString(", ") builder.WriteString("organization_id=") builder.WriteString(fmt.Sprintf("%v", at.OrganizationID)) + builder.WriteString(", ") + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", at.ProjectID)) builder.WriteByte(')') return builder.String() } diff --git a/app/controlplane/pkg/data/ent/apitoken/apitoken.go b/app/controlplane/pkg/data/ent/apitoken/apitoken.go index 5eb203347..dd87966e9 100644 --- a/app/controlplane/pkg/data/ent/apitoken/apitoken.go +++ b/app/controlplane/pkg/data/ent/apitoken/apitoken.go @@ -27,8 +27,12 @@ const ( FieldRevokedAt = "revoked_at" // FieldOrganizationID holds the string denoting the organization_id field in the database. FieldOrganizationID = "organization_id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" // EdgeOrganization holds the string denoting the organization edge name in mutations. EdgeOrganization = "organization" + // EdgeProject holds the string denoting the project edge name in mutations. + EdgeProject = "project" // Table holds the table name of the apitoken in the database. Table = "api_tokens" // OrganizationTable is the table that holds the organization relation/edge. @@ -38,6 +42,13 @@ const ( OrganizationInverseTable = "organizations" // OrganizationColumn is the table column denoting the organization relation/edge. OrganizationColumn = "organization_id" + // ProjectTable is the table that holds the project relation/edge. + ProjectTable = "api_tokens" + // ProjectInverseTable is the table name for the Project entity. + // It exists in this package in order to avoid circular dependency with the "project" package. + ProjectInverseTable = "projects" + // ProjectColumn is the table column denoting the project relation/edge. + ProjectColumn = "project_id" ) // Columns holds all SQL columns for apitoken fields. @@ -49,6 +60,7 @@ var Columns = []string{ FieldExpiresAt, FieldRevokedAt, FieldOrganizationID, + FieldProjectID, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -106,12 +118,24 @@ func ByOrganizationID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldOrganizationID, opts...).ToFunc() } +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + // ByOrganizationField orders the results by organization field. func ByOrganizationField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { sqlgraph.OrderByNeighborTerms(s, newOrganizationStep(), sql.OrderByField(field, opts...)) } } + +// ByProjectField orders the results by project field. +func ByProjectField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newProjectStep(), sql.OrderByField(field, opts...)) + } +} func newOrganizationStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -119,3 +143,10 @@ func newOrganizationStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, OrganizationTable, OrganizationColumn), ) } +func newProjectStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ProjectInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ProjectTable, ProjectColumn), + ) +} diff --git a/app/controlplane/pkg/data/ent/apitoken/where.go b/app/controlplane/pkg/data/ent/apitoken/where.go index 7e3005b9e..a2ded7928 100644 --- a/app/controlplane/pkg/data/ent/apitoken/where.go +++ b/app/controlplane/pkg/data/ent/apitoken/where.go @@ -86,6 +86,11 @@ func OrganizationID(v uuid.UUID) predicate.APIToken { return predicate.APIToken(sql.FieldEQ(FieldOrganizationID, v)) } +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.APIToken { + return predicate.APIToken(sql.FieldEQ(FieldProjectID, v)) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.APIToken { return predicate.APIToken(sql.FieldEQ(FieldName, v)) @@ -386,6 +391,36 @@ func OrganizationIDNotIn(vs ...uuid.UUID) predicate.APIToken { return predicate.APIToken(sql.FieldNotIn(FieldOrganizationID, vs...)) } +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.APIToken { + return predicate.APIToken(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.APIToken { + return predicate.APIToken(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.APIToken { + return predicate.APIToken(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.APIToken { + return predicate.APIToken(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDIsNil applies the IsNil predicate on the "project_id" field. +func ProjectIDIsNil() predicate.APIToken { + return predicate.APIToken(sql.FieldIsNull(FieldProjectID)) +} + +// ProjectIDNotNil applies the NotNil predicate on the "project_id" field. +func ProjectIDNotNil() predicate.APIToken { + return predicate.APIToken(sql.FieldNotNull(FieldProjectID)) +} + // HasOrganization applies the HasEdge predicate on the "organization" edge. func HasOrganization() predicate.APIToken { return predicate.APIToken(func(s *sql.Selector) { @@ -409,6 +444,29 @@ func HasOrganizationWith(preds ...predicate.Organization) predicate.APIToken { }) } +// HasProject applies the HasEdge predicate on the "project" edge. +func HasProject() predicate.APIToken { + return predicate.APIToken(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ProjectTable, ProjectColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasProjectWith applies the HasEdge predicate on the "project" edge with a given conditions (other predicates). +func HasProjectWith(preds ...predicate.Project) predicate.APIToken { + return predicate.APIToken(func(s *sql.Selector) { + step := newProjectStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.APIToken) predicate.APIToken { return predicate.APIToken(sql.AndPredicates(predicates...)) diff --git a/app/controlplane/pkg/data/ent/apitoken_create.go b/app/controlplane/pkg/data/ent/apitoken_create.go index 726720dfb..a8dd60b16 100644 --- a/app/controlplane/pkg/data/ent/apitoken_create.go +++ b/app/controlplane/pkg/data/ent/apitoken_create.go @@ -14,6 +14,7 @@ import ( "entgo.io/ent/schema/field" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" "github.com/google/uuid" ) @@ -93,6 +94,20 @@ func (atc *APITokenCreate) SetOrganizationID(u uuid.UUID) *APITokenCreate { return atc } +// SetProjectID sets the "project_id" field. +func (atc *APITokenCreate) SetProjectID(u uuid.UUID) *APITokenCreate { + atc.mutation.SetProjectID(u) + return atc +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (atc *APITokenCreate) SetNillableProjectID(u *uuid.UUID) *APITokenCreate { + if u != nil { + atc.SetProjectID(*u) + } + return atc +} + // SetID sets the "id" field. func (atc *APITokenCreate) SetID(u uuid.UUID) *APITokenCreate { atc.mutation.SetID(u) @@ -112,6 +127,11 @@ func (atc *APITokenCreate) SetOrganization(o *Organization) *APITokenCreate { return atc.SetOrganizationID(o.ID) } +// SetProject sets the "project" edge to the Project entity. +func (atc *APITokenCreate) SetProject(p *Project) *APITokenCreate { + return atc.SetProjectID(p.ID) +} + // Mutation returns the APITokenMutation object of the builder. func (atc *APITokenCreate) Mutation() *APITokenMutation { return atc.mutation @@ -244,6 +264,23 @@ func (atc *APITokenCreate) createSpec() (*APIToken, *sqlgraph.CreateSpec) { _node.OrganizationID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := atc.mutation.ProjectIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: apitoken.ProjectTable, + Columns: []string{apitoken.ProjectColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(project.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ProjectID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } @@ -362,6 +399,24 @@ func (u *APITokenUpsert) UpdateOrganizationID() *APITokenUpsert { return u } +// SetProjectID sets the "project_id" field. +func (u *APITokenUpsert) SetProjectID(v uuid.UUID) *APITokenUpsert { + u.Set(apitoken.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *APITokenUpsert) UpdateProjectID() *APITokenUpsert { + u.SetExcluded(apitoken.FieldProjectID) + return u +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *APITokenUpsert) ClearProjectID() *APITokenUpsert { + u.SetNull(apitoken.FieldProjectID) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -493,6 +548,27 @@ func (u *APITokenUpsertOne) UpdateOrganizationID() *APITokenUpsertOne { }) } +// SetProjectID sets the "project_id" field. +func (u *APITokenUpsertOne) SetProjectID(v uuid.UUID) *APITokenUpsertOne { + return u.Update(func(s *APITokenUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *APITokenUpsertOne) UpdateProjectID() *APITokenUpsertOne { + return u.Update(func(s *APITokenUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *APITokenUpsertOne) ClearProjectID() *APITokenUpsertOne { + return u.Update(func(s *APITokenUpsert) { + s.ClearProjectID() + }) +} + // Exec executes the query. func (u *APITokenUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -791,6 +867,27 @@ func (u *APITokenUpsertBulk) UpdateOrganizationID() *APITokenUpsertBulk { }) } +// SetProjectID sets the "project_id" field. +func (u *APITokenUpsertBulk) SetProjectID(v uuid.UUID) *APITokenUpsertBulk { + return u.Update(func(s *APITokenUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *APITokenUpsertBulk) UpdateProjectID() *APITokenUpsertBulk { + return u.Update(func(s *APITokenUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *APITokenUpsertBulk) ClearProjectID() *APITokenUpsertBulk { + return u.Update(func(s *APITokenUpsert) { + s.ClearProjectID() + }) +} + // Exec executes the query. func (u *APITokenUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/app/controlplane/pkg/data/ent/apitoken_query.go b/app/controlplane/pkg/data/ent/apitoken_query.go index 6d48659d6..779152434 100644 --- a/app/controlplane/pkg/data/ent/apitoken_query.go +++ b/app/controlplane/pkg/data/ent/apitoken_query.go @@ -15,6 +15,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" "github.com/google/uuid" ) @@ -26,6 +27,7 @@ type APITokenQuery struct { inters []Interceptor predicates []predicate.APIToken withOrganization *OrganizationQuery + withProject *ProjectQuery modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -85,6 +87,28 @@ func (atq *APITokenQuery) QueryOrganization() *OrganizationQuery { return query } +// QueryProject chains the current query on the "project" edge. +func (atq *APITokenQuery) QueryProject() *ProjectQuery { + query := (&ProjectClient{config: atq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := atq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := atq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(apitoken.Table, apitoken.FieldID, selector), + sqlgraph.To(project.Table, project.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, apitoken.ProjectTable, apitoken.ProjectColumn), + ) + fromU = sqlgraph.SetNeighbors(atq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first APIToken entity from the query. // Returns a *NotFoundError when no APIToken was found. func (atq *APITokenQuery) First(ctx context.Context) (*APIToken, error) { @@ -278,6 +302,7 @@ func (atq *APITokenQuery) Clone() *APITokenQuery { inters: append([]Interceptor{}, atq.inters...), predicates: append([]predicate.APIToken{}, atq.predicates...), withOrganization: atq.withOrganization.Clone(), + withProject: atq.withProject.Clone(), // clone intermediate query. sql: atq.sql.Clone(), path: atq.path, @@ -296,6 +321,17 @@ func (atq *APITokenQuery) WithOrganization(opts ...func(*OrganizationQuery)) *AP return atq } +// WithProject tells the query-builder to eager-load the nodes that are connected to +// the "project" edge. The optional arguments are used to configure the query builder of the edge. +func (atq *APITokenQuery) WithProject(opts ...func(*ProjectQuery)) *APITokenQuery { + query := (&ProjectClient{config: atq.config}).Query() + for _, opt := range opts { + opt(query) + } + atq.withProject = query + return atq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -374,8 +410,9 @@ func (atq *APITokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*AP var ( nodes = []*APIToken{} _spec = atq.querySpec() - loadedTypes = [1]bool{ + loadedTypes = [2]bool{ atq.withOrganization != nil, + atq.withProject != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -405,6 +442,12 @@ func (atq *APITokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*AP return nil, err } } + if query := atq.withProject; query != nil { + if err := atq.loadProject(ctx, query, nodes, nil, + func(n *APIToken, e *Project) { n.Edges.Project = e }); err != nil { + return nil, err + } + } return nodes, nil } @@ -437,6 +480,35 @@ func (atq *APITokenQuery) loadOrganization(ctx context.Context, query *Organizat } return nil } +func (atq *APITokenQuery) loadProject(ctx context.Context, query *ProjectQuery, nodes []*APIToken, init func(*APIToken), assign func(*APIToken, *Project)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*APIToken) + for i := range nodes { + fk := nodes[i].ProjectID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(project.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "project_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} func (atq *APITokenQuery) sqlCount(ctx context.Context) (int, error) { _spec := atq.querySpec() @@ -469,6 +541,9 @@ func (atq *APITokenQuery) querySpec() *sqlgraph.QuerySpec { if atq.withOrganization != nil { _spec.Node.AddColumnOnce(apitoken.FieldOrganizationID) } + if atq.withProject != nil { + _spec.Node.AddColumnOnce(apitoken.FieldProjectID) + } } if ps := atq.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/app/controlplane/pkg/data/ent/apitoken_update.go b/app/controlplane/pkg/data/ent/apitoken_update.go index aff05a858..ce24b2ed4 100644 --- a/app/controlplane/pkg/data/ent/apitoken_update.go +++ b/app/controlplane/pkg/data/ent/apitoken_update.go @@ -14,6 +14,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" "github.com/google/uuid" ) @@ -105,11 +106,36 @@ func (atu *APITokenUpdate) SetNillableOrganizationID(u *uuid.UUID) *APITokenUpda return atu } +// SetProjectID sets the "project_id" field. +func (atu *APITokenUpdate) SetProjectID(u uuid.UUID) *APITokenUpdate { + atu.mutation.SetProjectID(u) + return atu +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (atu *APITokenUpdate) SetNillableProjectID(u *uuid.UUID) *APITokenUpdate { + if u != nil { + atu.SetProjectID(*u) + } + return atu +} + +// ClearProjectID clears the value of the "project_id" field. +func (atu *APITokenUpdate) ClearProjectID() *APITokenUpdate { + atu.mutation.ClearProjectID() + return atu +} + // SetOrganization sets the "organization" edge to the Organization entity. func (atu *APITokenUpdate) SetOrganization(o *Organization) *APITokenUpdate { return atu.SetOrganizationID(o.ID) } +// SetProject sets the "project" edge to the Project entity. +func (atu *APITokenUpdate) SetProject(p *Project) *APITokenUpdate { + return atu.SetProjectID(p.ID) +} + // Mutation returns the APITokenMutation object of the builder. func (atu *APITokenUpdate) Mutation() *APITokenMutation { return atu.mutation @@ -121,6 +147,12 @@ func (atu *APITokenUpdate) ClearOrganization() *APITokenUpdate { return atu } +// ClearProject clears the "project" edge to the Project entity. +func (atu *APITokenUpdate) ClearProject() *APITokenUpdate { + atu.mutation.ClearProject() + return atu +} + // Save executes the query and returns the number of nodes affected by the update operation. func (atu *APITokenUpdate) Save(ctx context.Context) (int, error) { return withHooks(ctx, atu.sqlSave, atu.mutation, atu.hooks) @@ -221,6 +253,35 @@ func (atu *APITokenUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if atu.mutation.ProjectCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: apitoken.ProjectTable, + Columns: []string{apitoken.ProjectColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(project.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := atu.mutation.ProjectIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: apitoken.ProjectTable, + Columns: []string{apitoken.ProjectColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(project.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _spec.AddModifiers(atu.modifiers...) if n, err = sqlgraph.UpdateNodes(ctx, atu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { @@ -317,11 +378,36 @@ func (atuo *APITokenUpdateOne) SetNillableOrganizationID(u *uuid.UUID) *APIToken return atuo } +// SetProjectID sets the "project_id" field. +func (atuo *APITokenUpdateOne) SetProjectID(u uuid.UUID) *APITokenUpdateOne { + atuo.mutation.SetProjectID(u) + return atuo +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (atuo *APITokenUpdateOne) SetNillableProjectID(u *uuid.UUID) *APITokenUpdateOne { + if u != nil { + atuo.SetProjectID(*u) + } + return atuo +} + +// ClearProjectID clears the value of the "project_id" field. +func (atuo *APITokenUpdateOne) ClearProjectID() *APITokenUpdateOne { + atuo.mutation.ClearProjectID() + return atuo +} + // SetOrganization sets the "organization" edge to the Organization entity. func (atuo *APITokenUpdateOne) SetOrganization(o *Organization) *APITokenUpdateOne { return atuo.SetOrganizationID(o.ID) } +// SetProject sets the "project" edge to the Project entity. +func (atuo *APITokenUpdateOne) SetProject(p *Project) *APITokenUpdateOne { + return atuo.SetProjectID(p.ID) +} + // Mutation returns the APITokenMutation object of the builder. func (atuo *APITokenUpdateOne) Mutation() *APITokenMutation { return atuo.mutation @@ -333,6 +419,12 @@ func (atuo *APITokenUpdateOne) ClearOrganization() *APITokenUpdateOne { return atuo } +// ClearProject clears the "project" edge to the Project entity. +func (atuo *APITokenUpdateOne) ClearProject() *APITokenUpdateOne { + atuo.mutation.ClearProject() + return atuo +} + // Where appends a list predicates to the APITokenUpdate builder. func (atuo *APITokenUpdateOne) Where(ps ...predicate.APIToken) *APITokenUpdateOne { atuo.mutation.Where(ps...) @@ -463,6 +555,35 @@ func (atuo *APITokenUpdateOne) sqlSave(ctx context.Context) (_node *APIToken, er } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if atuo.mutation.ProjectCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: apitoken.ProjectTable, + Columns: []string{apitoken.ProjectColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(project.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := atuo.mutation.ProjectIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: apitoken.ProjectTable, + Columns: []string{apitoken.ProjectColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(project.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _spec.AddModifiers(atuo.modifiers...) _node = &APIToken{config: atuo.config} _spec.Assign = _node.assignValues diff --git a/app/controlplane/pkg/data/ent/client.go b/app/controlplane/pkg/data/ent/client.go index 5438d6100..3bb03af6f 100644 --- a/app/controlplane/pkg/data/ent/client.go +++ b/app/controlplane/pkg/data/ent/client.go @@ -490,6 +490,22 @@ func (c *APITokenClient) QueryOrganization(at *APIToken) *OrganizationQuery { return query } +// QueryProject queries the project edge of a APIToken. +func (c *APITokenClient) QueryProject(at *APIToken) *ProjectQuery { + query := (&ProjectClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := at.ID + step := sqlgraph.NewStep( + sqlgraph.From(apitoken.Table, apitoken.FieldID, id), + sqlgraph.To(project.Table, project.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, apitoken.ProjectTable, apitoken.ProjectColumn), + ) + fromV = sqlgraph.Neighbors(at.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *APITokenClient) Hooks() []Hook { return c.hooks.APIToken diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20250626100818.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20250626100818.sql new file mode 100644 index 000000000..07155bc60 --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20250626100818.sql @@ -0,0 +1,8 @@ +-- Drop index "apitoken_name_organization_id" from table: "api_tokens" +DROP INDEX "apitoken_name_organization_id"; +-- Modify "api_tokens" table +ALTER TABLE "api_tokens" ADD COLUMN "project_id" uuid NULL; +-- Create index "apitoken_name_organization_id" to table: "api_tokens" +CREATE UNIQUE INDEX "apitoken_name_organization_id" ON "api_tokens" ("name", "organization_id") WHERE ((revoked_at IS NULL) AND (project_id IS NULL)); +-- Create index "apitoken_name_project_id" to table: "api_tokens" +CREATE UNIQUE INDEX "apitoken_name_project_id" ON "api_tokens" ("name", "project_id") WHERE ((revoked_at IS NULL) AND (project_id IS NOT NULL)); diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum index 41caeab51..01eb21223 100644 --- a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:vK06zG9gBHFaCg/1y+KI+CeDOp/rjLd1pTWjHNRXKfQ= +h1:HpurNcc1acoT8CS+31qjqNUFOzNYfsdA0NuiuZfj/lY= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -89,3 +89,4 @@ h1:vK06zG9gBHFaCg/1y+KI+CeDOp/rjLd1pTWjHNRXKfQ= 20250616182058.sql h1:fg5r2AZPj/n9y+FeCRaieUrj0UdFzBooLg7xJsNz8P0= 20250617182716.sql h1:APJGiHfWf95qNV622cc367xde2kEn217BUH+za58vxc= 20250626061546.sql h1:eJSVav8H0qrar65KBkmTPkQTxmNExdo+zmVSpF2Zt5E= +20250626100818.sql h1:NE/5ZyT4zQFTsUyDbE74GzBGJOMZfdBhujHtnBZ0teU= diff --git a/app/controlplane/pkg/data/ent/migrate/schema.go b/app/controlplane/pkg/data/ent/migrate/schema.go index b6f14698b..2b6567cbb 100644 --- a/app/controlplane/pkg/data/ent/migrate/schema.go +++ b/app/controlplane/pkg/data/ent/migrate/schema.go @@ -17,6 +17,7 @@ var ( {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, {Name: "expires_at", Type: field.TypeTime, Nullable: true}, {Name: "revoked_at", Type: field.TypeTime, Nullable: true}, + {Name: "project_id", Type: field.TypeUUID, Nullable: true}, {Name: "organization_id", Type: field.TypeUUID}, } // APITokensTable holds the schema information for the "api_tokens" table. @@ -26,8 +27,14 @@ var ( PrimaryKey: []*schema.Column{APITokensColumns[0]}, ForeignKeys: []*schema.ForeignKey{ { - Symbol: "api_tokens_organizations_api_tokens", + Symbol: "api_tokens_projects_project", Columns: []*schema.Column{APITokensColumns[6]}, + RefColumns: []*schema.Column{ProjectsColumns[0]}, + OnDelete: schema.SetNull, + }, + { + Symbol: "api_tokens_organizations_api_tokens", + Columns: []*schema.Column{APITokensColumns[7]}, RefColumns: []*schema.Column{OrganizationsColumns[0]}, OnDelete: schema.Cascade, }, @@ -36,9 +43,17 @@ var ( { Name: "apitoken_name_organization_id", Unique: true, + Columns: []*schema.Column{APITokensColumns[1], APITokensColumns[7]}, + Annotation: &entsql.IndexAnnotation{ + Where: "revoked_at IS NULL AND project_id IS NULL", + }, + }, + { + Name: "apitoken_name_project_id", + Unique: true, Columns: []*schema.Column{APITokensColumns[1], APITokensColumns[6]}, Annotation: &entsql.IndexAnnotation{ - Where: "revoked_at IS NULL", + Where: "revoked_at IS NULL AND project_id IS NOT NULL", }, }, }, @@ -880,7 +895,8 @@ var ( ) func init() { - APITokensTable.ForeignKeys[0].RefTable = OrganizationsTable + APITokensTable.ForeignKeys[0].RefTable = ProjectsTable + APITokensTable.ForeignKeys[1].RefTable = OrganizationsTable AttestationsTable.ForeignKeys[0].RefTable = WorkflowRunsTable CasBackendsTable.ForeignKeys[0].RefTable = OrganizationsTable CasMappingsTable.ForeignKeys[0].RefTable = CasBackendsTable diff --git a/app/controlplane/pkg/data/ent/mutation.go b/app/controlplane/pkg/data/ent/mutation.go index 53505e6d6..1efc9e578 100644 --- a/app/controlplane/pkg/data/ent/mutation.go +++ b/app/controlplane/pkg/data/ent/mutation.go @@ -84,6 +84,8 @@ type APITokenMutation struct { clearedFields map[string]struct{} organization *uuid.UUID clearedorganization bool + project *uuid.UUID + clearedproject bool done bool oldValue func(context.Context) (*APIToken, error) predicates []predicate.APIToken @@ -448,6 +450,55 @@ func (m *APITokenMutation) ResetOrganizationID() { m.organization = nil } +// SetProjectID sets the "project_id" field. +func (m *APITokenMutation) SetProjectID(u uuid.UUID) { + m.project = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *APITokenMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the APIToken entity. +// If the APIToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *APITokenMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ClearProjectID clears the value of the "project_id" field. +func (m *APITokenMutation) ClearProjectID() { + m.project = nil + m.clearedFields[apitoken.FieldProjectID] = struct{}{} +} + +// ProjectIDCleared returns if the "project_id" field was cleared in this mutation. +func (m *APITokenMutation) ProjectIDCleared() bool { + _, ok := m.clearedFields[apitoken.FieldProjectID] + return ok +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *APITokenMutation) ResetProjectID() { + m.project = nil + delete(m.clearedFields, apitoken.FieldProjectID) +} + // ClearOrganization clears the "organization" edge to the Organization entity. func (m *APITokenMutation) ClearOrganization() { m.clearedorganization = true @@ -475,6 +526,33 @@ func (m *APITokenMutation) ResetOrganization() { m.clearedorganization = false } +// ClearProject clears the "project" edge to the Project entity. +func (m *APITokenMutation) ClearProject() { + m.clearedproject = true + m.clearedFields[apitoken.FieldProjectID] = struct{}{} +} + +// ProjectCleared reports if the "project" edge to the Project entity was cleared. +func (m *APITokenMutation) ProjectCleared() bool { + return m.ProjectIDCleared() || m.clearedproject +} + +// ProjectIDs returns the "project" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ProjectID instead. It exists only for internal usage by the builders. +func (m *APITokenMutation) ProjectIDs() (ids []uuid.UUID) { + if id := m.project; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetProject resets all changes to the "project" edge. +func (m *APITokenMutation) ResetProject() { + m.project = nil + m.clearedproject = false +} + // Where appends a list predicates to the APITokenMutation builder. func (m *APITokenMutation) Where(ps ...predicate.APIToken) { m.predicates = append(m.predicates, ps...) @@ -509,7 +587,7 @@ func (m *APITokenMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *APITokenMutation) Fields() []string { - fields := make([]string, 0, 6) + fields := make([]string, 0, 7) if m.name != nil { fields = append(fields, apitoken.FieldName) } @@ -528,6 +606,9 @@ func (m *APITokenMutation) Fields() []string { if m.organization != nil { fields = append(fields, apitoken.FieldOrganizationID) } + if m.project != nil { + fields = append(fields, apitoken.FieldProjectID) + } return fields } @@ -548,6 +629,8 @@ func (m *APITokenMutation) Field(name string) (ent.Value, bool) { return m.RevokedAt() case apitoken.FieldOrganizationID: return m.OrganizationID() + case apitoken.FieldProjectID: + return m.ProjectID() } return nil, false } @@ -569,6 +652,8 @@ func (m *APITokenMutation) OldField(ctx context.Context, name string) (ent.Value return m.OldRevokedAt(ctx) case apitoken.FieldOrganizationID: return m.OldOrganizationID(ctx) + case apitoken.FieldProjectID: + return m.OldProjectID(ctx) } return nil, fmt.Errorf("unknown APIToken field %s", name) } @@ -620,6 +705,13 @@ func (m *APITokenMutation) SetField(name string, value ent.Value) error { } m.SetOrganizationID(v) return nil + case apitoken.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil } return fmt.Errorf("unknown APIToken field %s", name) } @@ -659,6 +751,9 @@ func (m *APITokenMutation) ClearedFields() []string { if m.FieldCleared(apitoken.FieldRevokedAt) { fields = append(fields, apitoken.FieldRevokedAt) } + if m.FieldCleared(apitoken.FieldProjectID) { + fields = append(fields, apitoken.FieldProjectID) + } return fields } @@ -682,6 +777,9 @@ func (m *APITokenMutation) ClearField(name string) error { case apitoken.FieldRevokedAt: m.ClearRevokedAt() return nil + case apitoken.FieldProjectID: + m.ClearProjectID() + return nil } return fmt.Errorf("unknown APIToken nullable field %s", name) } @@ -708,16 +806,22 @@ func (m *APITokenMutation) ResetField(name string) error { case apitoken.FieldOrganizationID: m.ResetOrganizationID() return nil + case apitoken.FieldProjectID: + m.ResetProjectID() + return nil } return fmt.Errorf("unknown APIToken field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. func (m *APITokenMutation) AddedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 2) if m.organization != nil { edges = append(edges, apitoken.EdgeOrganization) } + if m.project != nil { + edges = append(edges, apitoken.EdgeProject) + } return edges } @@ -729,13 +833,17 @@ func (m *APITokenMutation) AddedIDs(name string) []ent.Value { if id := m.organization; id != nil { return []ent.Value{*id} } + case apitoken.EdgeProject: + if id := m.project; id != nil { + return []ent.Value{*id} + } } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *APITokenMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 2) return edges } @@ -747,10 +855,13 @@ func (m *APITokenMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *APITokenMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 2) if m.clearedorganization { edges = append(edges, apitoken.EdgeOrganization) } + if m.clearedproject { + edges = append(edges, apitoken.EdgeProject) + } return edges } @@ -760,6 +871,8 @@ func (m *APITokenMutation) EdgeCleared(name string) bool { switch name { case apitoken.EdgeOrganization: return m.clearedorganization + case apitoken.EdgeProject: + return m.clearedproject } return false } @@ -771,6 +884,9 @@ func (m *APITokenMutation) ClearEdge(name string) error { case apitoken.EdgeOrganization: m.ClearOrganization() return nil + case apitoken.EdgeProject: + m.ClearProject() + return nil } return fmt.Errorf("unknown APIToken unique edge %s", name) } @@ -782,6 +898,9 @@ func (m *APITokenMutation) ResetEdge(name string) error { case apitoken.EdgeOrganization: m.ResetOrganization() return nil + case apitoken.EdgeProject: + m.ResetProject() + return nil } return fmt.Errorf("unknown APIToken edge %s", name) } diff --git a/app/controlplane/pkg/data/ent/schema-viz.html b/app/controlplane/pkg/data/ent/schema-viz.html index 20d8ad658..4ce94daee 100644 --- a/app/controlplane/pkg/data/ent/schema-viz.html +++ b/app/controlplane/pkg/data/ent/schema-viz.html @@ -70,7 +70,7 @@ } - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Group\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"GroupMembership\",\"fields\":[{\"name\":\"group_id\",\"type\":\"uuid.UUID\"},{\"name\":\"user_id\",\"type\":\"uuid.UUID\"},{\"name\":\"maintainer\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"Group\",\"to\":\"User\",\"label\":\"members\"},{\"from\":\"GroupMembership\",\"to\":\"Group\",\"label\":\"group\"},{\"from\":\"GroupMembership\",\"to\":\"User\",\"label\":\"user\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Organization\",\"to\":\"Group\",\"label\":\"groups\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Group\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"GroupMembership\",\"fields\":[{\"name\":\"group_id\",\"type\":\"uuid.UUID\"},{\"name\":\"user_id\",\"type\":\"uuid.UUID\"},{\"name\":\"maintainer\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"APIToken\",\"to\":\"Project\",\"label\":\"project\"},{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"Group\",\"to\":\"User\",\"label\":\"members\"},{\"from\":\"GroupMembership\",\"to\":\"Group\",\"label\":\"group\"},{\"from\":\"GroupMembership\",\"to\":\"User\",\"label\":\"user\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Organization\",\"to\":\"Group\",\"label\":\"groups\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, diff --git a/app/controlplane/pkg/data/ent/schema/apitoken.go b/app/controlplane/pkg/data/ent/schema/apitoken.go index e1762abea..9c27d6fdc 100644 --- a/app/controlplane/pkg/data/ent/schema/apitoken.go +++ b/app/controlplane/pkg/data/ent/schema/apitoken.go @@ -1,5 +1,5 @@ // -// Copyright 2023 The Chainloop Authors. +// Copyright 2023-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,20 +43,30 @@ func (APIToken) Fields() []ent.Field { // the token can be manually revoked field.Time("revoked_at").Optional(), field.UUID("organization_id", uuid.UUID{}), + // Tokens can be associated with a project + // if this value is not set, the token is an organization level token + field.UUID("project_id", uuid.UUID{}).Optional(), } } func (APIToken) Edges() []ent.Edge { return []ent.Edge{ edge.From("organization", Organization.Type).Field("organization_id").Ref("api_tokens").Unique().Required(), + edge.To("project", Project.Type).Field("project_id").Unique(), } } func (APIToken) Indexes() []ent.Index { return []ent.Index{ // names are unique within a organization and affects only to non-deleted items + // These are for org level tokens index.Fields("name").Edges("organization").Unique().Annotations( - entsql.IndexWhere("revoked_at IS NULL"), + entsql.IndexWhere("revoked_at IS NULL AND project_id IS NULL"), + ), + + // for project level tokens, we scope the uniqueness to the organization and project + index.Fields("name").Edges("project").Unique().Annotations( + entsql.IndexWhere("revoked_at IS NULL AND project_id IS NOT NULL"), ), } } From c5e55d5c39bc7fd1009bdcf461603add82d5420d Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 13:49:16 +0200 Subject: [PATCH 02/13] update token Signed-off-by: Miguel Martinez --- app/cli/internal/action/apitoken_create.go | 18 +- .../api/controlplane/v1/api_token.pb.go | 340 +++----- .../api/controlplane/v1/api_token.proto | 12 +- .../api/controlplane/v1/project.pb.go | 647 +++++++++++++++ .../api/controlplane/v1/project.proto | 68 ++ .../api/controlplane/v1/project_grpc.pb.go | 200 +++++ .../controlplane/v1/response_messages.pb.go | 293 +++++-- .../controlplane/v1/response_messages.proto | 13 + .../gen/frontend/controlplane/v1/api_token.ts | 193 +---- .../gen/frontend/controlplane/v1/project.ts | 762 ++++++++++++++++++ .../controlplane/v1/response_messages.ts | 199 +++++ ...ntrolplane.v1.APITokenItem.jsonschema.json | 12 + .../controlplane.v1.APITokenItem.schema.json | 12 + ...rviceAPITokenCreateRequest.jsonschema.json | 32 + ...ctServiceAPITokenCreateRequest.schema.json | 32 + ...reateResponse.APITokenFull.jsonschema.json | 15 + ...kenCreateResponse.APITokenFull.schema.json | 15 + ...viceAPITokenCreateResponse.jsonschema.json | 12 + ...tServiceAPITokenCreateResponse.schema.json | 12 + ...ServiceAPITokenListRequest.jsonschema.json | 17 + ...jectServiceAPITokenListRequest.schema.json | 17 + ...erviceAPITokenListResponse.jsonschema.json | 15 + ...ectServiceAPITokenListResponse.schema.json | 15 + ...rviceAPITokenRevokeRequest.jsonschema.json | 12 + ...ctServiceAPITokenRevokeRequest.schema.json | 12 + ...viceAPITokenRevokeResponse.jsonschema.json | 8 + ...tServiceAPITokenRevokeResponse.schema.json | 8 + app/controlplane/internal/service/apitoken.go | 8 + app/controlplane/pkg/biz/apitoken.go | 5 +- app/controlplane/pkg/data/apitoken.go | 13 +- 30 files changed, 2500 insertions(+), 517 deletions(-) create mode 100644 app/controlplane/api/controlplane/v1/project.pb.go create mode 100644 app/controlplane/api/controlplane/v1/project.proto create mode 100644 app/controlplane/api/controlplane/v1/project_grpc.pb.go create mode 100644 app/controlplane/api/gen/frontend/controlplane/v1/project.ts create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.schema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.schema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.schema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.schema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.jsonschema.json create mode 100644 app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.schema.json diff --git a/app/cli/internal/action/apitoken_create.go b/app/cli/internal/action/apitoken_create.go index 7c3a7b58a..ad2a2fd3f 100644 --- a/app/cli/internal/action/apitoken_create.go +++ b/app/cli/internal/action/apitoken_create.go @@ -62,10 +62,12 @@ type APITokenItem struct { Name string `json:"name"` Description string `json:"description"` // JWT is returned only during the creation - JWT string `json:"jwt,omitempty"` - CreatedAt *time.Time `json:"createdAt"` - RevokedAt *time.Time `json:"revokedAt,omitempty"` - ExpiresAt *time.Time `json:"expiresAt,omitempty"` + JWT string `json:"jwt,omitempty"` + CreatedAt *time.Time `json:"createdAt"` + RevokedAt *time.Time `json:"revokedAt,omitempty"` + ExpiresAt *time.Time `json:"expiresAt,omitempty"` + ProjectID string `json:"projectId,omitempty"` + ProjectName string `json:"projectName,omitempty"` } func pbAPITokenItemToAPITokenItem(p *pb.APITokenItem) *APITokenItem { @@ -88,5 +90,13 @@ func pbAPITokenItemToAPITokenItem(p *pb.APITokenItem) *APITokenItem { item.ExpiresAt = toTimePtr(p.ExpiresAt.AsTime()) } + if p.ProjectId != "" { + item.ProjectID = p.ProjectId + } + + if p.ProjectName != "" { + item.ProjectName = p.ProjectName + } + return item } diff --git a/app/controlplane/api/controlplane/v1/api_token.pb.go b/app/controlplane/api/controlplane/v1/api_token.pb.go index 21670ed1b..835bb5907 100644 --- a/app/controlplane/api/controlplane/v1/api_token.pb.go +++ b/app/controlplane/api/controlplane/v1/api_token.pb.go @@ -26,7 +26,7 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" + _ "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -327,109 +327,6 @@ func (x *APITokenServiceListResponse) GetResult() []*APITokenItem { return nil } -type APITokenItem struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - OrganizationId string `protobuf:"bytes,3,opt,name=organization_id,json=organizationId,proto3" json:"organization_id,omitempty"` - OrganizationName string `protobuf:"bytes,8,opt,name=organization_name,json=organizationName,proto3" json:"organization_name,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - RevokedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=revoked_at,json=revokedAt,proto3" json:"revoked_at,omitempty"` - ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` -} - -func (x *APITokenItem) Reset() { - *x = APITokenItem{} - if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_api_token_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *APITokenItem) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*APITokenItem) ProtoMessage() {} - -func (x *APITokenItem) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_api_token_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use APITokenItem.ProtoReflect.Descriptor instead. -func (*APITokenItem) Descriptor() ([]byte, []int) { - return file_controlplane_v1_api_token_proto_rawDescGZIP(), []int{6} -} - -func (x *APITokenItem) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *APITokenItem) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *APITokenItem) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *APITokenItem) GetOrganizationId() string { - if x != nil { - return x.OrganizationId - } - return "" -} - -func (x *APITokenItem) GetOrganizationName() string { - if x != nil { - return x.OrganizationName - } - return "" -} - -func (x *APITokenItem) GetCreatedAt() *timestamppb.Timestamp { - if x != nil { - return x.CreatedAt - } - return nil -} - -func (x *APITokenItem) GetRevokedAt() *timestamppb.Timestamp { - if x != nil { - return x.RevokedAt - } - return nil -} - -func (x *APITokenItem) GetExpiresAt() *timestamppb.Timestamp { - if x != nil { - return x.ExpiresAt - } - return nil -} - type APITokenServiceCreateResponse_APITokenFull struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -442,7 +339,7 @@ type APITokenServiceCreateResponse_APITokenFull struct { func (x *APITokenServiceCreateResponse_APITokenFull) Reset() { *x = APITokenServiceCreateResponse_APITokenFull{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_api_token_proto_msgTypes[7] + mi := &file_controlplane_v1_api_token_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -455,7 +352,7 @@ func (x *APITokenServiceCreateResponse_APITokenFull) String() string { func (*APITokenServiceCreateResponse_APITokenFull) ProtoMessage() {} func (x *APITokenServiceCreateResponse_APITokenFull) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_api_token_proto_msgTypes[7] + mi := &file_controlplane_v1_api_token_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -493,107 +390,87 @@ var file_controlplane_v1_api_token_proto_rawDesc = []byte{ 0x6f, 0x12, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xc0, 0x01, 0x0a, 0x1c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, - 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x5f, 0x69, 0x6e, 0x22, 0xc9, 0x01, 0x0a, 0x1d, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, - 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, - 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, - 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, - 0xb8, 0x01, 0x0a, 0x1c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, - 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, - 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, - 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, - 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, - 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x41, 0x50, - 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, - 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x0a, 0x1a, 0x41, - 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, - 0x65, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x01, 0x0a, 0x1c, 0x41, 0x50, + 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, + 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, + 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, + 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, + 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x22, 0xc9, 0x01, 0x0a, + 0x1d, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, + 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, + 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, + 0x75, 0x6c, 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, - 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xdb, 0x02, 0x0a, 0x0c, 0x41, 0x50, 0x49, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, 0x67, 0x61, - 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x39, 0x0a, 0x0a, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x32, 0xc6, 0x02, 0x0a, 0x0f, 0x41, 0x50, 0x49, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, - 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, 0xb8, 0x01, 0x0a, 0x1c, 0x41, 0x50, 0x49, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, + 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, + 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, + 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, + 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, + 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x0a, 0x1a, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, + 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x41, + 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x32, 0xc6, 0x02, 0x0a, 0x0f, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, + 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, + 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x67, 0x0a, 0x06, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, - 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, - 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, + 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, + 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, + 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, + 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -608,7 +485,7 @@ func file_controlplane_v1_api_token_proto_rawDescGZIP() []byte { return file_controlplane_v1_api_token_proto_rawDescData } -var file_controlplane_v1_api_token_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_controlplane_v1_api_token_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_controlplane_v1_api_token_proto_goTypes = []interface{}{ (*APITokenServiceCreateRequest)(nil), // 0: controlplane.v1.APITokenServiceCreateRequest (*APITokenServiceCreateResponse)(nil), // 1: controlplane.v1.APITokenServiceCreateResponse @@ -616,30 +493,26 @@ var file_controlplane_v1_api_token_proto_goTypes = []interface{}{ (*APITokenServiceRevokeResponse)(nil), // 3: controlplane.v1.APITokenServiceRevokeResponse (*APITokenServiceListRequest)(nil), // 4: controlplane.v1.APITokenServiceListRequest (*APITokenServiceListResponse)(nil), // 5: controlplane.v1.APITokenServiceListResponse - (*APITokenItem)(nil), // 6: controlplane.v1.APITokenItem - (*APITokenServiceCreateResponse_APITokenFull)(nil), // 7: controlplane.v1.APITokenServiceCreateResponse.APITokenFull - (*durationpb.Duration)(nil), // 8: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp + (*APITokenServiceCreateResponse_APITokenFull)(nil), // 6: controlplane.v1.APITokenServiceCreateResponse.APITokenFull + (*durationpb.Duration)(nil), // 7: google.protobuf.Duration + (*APITokenItem)(nil), // 8: controlplane.v1.APITokenItem } var file_controlplane_v1_api_token_proto_depIdxs = []int32{ - 8, // 0: controlplane.v1.APITokenServiceCreateRequest.expires_in:type_name -> google.protobuf.Duration - 7, // 1: controlplane.v1.APITokenServiceCreateResponse.result:type_name -> controlplane.v1.APITokenServiceCreateResponse.APITokenFull - 6, // 2: controlplane.v1.APITokenServiceListResponse.result:type_name -> controlplane.v1.APITokenItem - 9, // 3: controlplane.v1.APITokenItem.created_at:type_name -> google.protobuf.Timestamp - 9, // 4: controlplane.v1.APITokenItem.revoked_at:type_name -> google.protobuf.Timestamp - 9, // 5: controlplane.v1.APITokenItem.expires_at:type_name -> google.protobuf.Timestamp - 6, // 6: controlplane.v1.APITokenServiceCreateResponse.APITokenFull.item:type_name -> controlplane.v1.APITokenItem - 0, // 7: controlplane.v1.APITokenService.Create:input_type -> controlplane.v1.APITokenServiceCreateRequest - 4, // 8: controlplane.v1.APITokenService.List:input_type -> controlplane.v1.APITokenServiceListRequest - 2, // 9: controlplane.v1.APITokenService.Revoke:input_type -> controlplane.v1.APITokenServiceRevokeRequest - 1, // 10: controlplane.v1.APITokenService.Create:output_type -> controlplane.v1.APITokenServiceCreateResponse - 5, // 11: controlplane.v1.APITokenService.List:output_type -> controlplane.v1.APITokenServiceListResponse - 3, // 12: controlplane.v1.APITokenService.Revoke:output_type -> controlplane.v1.APITokenServiceRevokeResponse - 10, // [10:13] is the sub-list for method output_type - 7, // [7:10] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 7, // 0: controlplane.v1.APITokenServiceCreateRequest.expires_in:type_name -> google.protobuf.Duration + 6, // 1: controlplane.v1.APITokenServiceCreateResponse.result:type_name -> controlplane.v1.APITokenServiceCreateResponse.APITokenFull + 8, // 2: controlplane.v1.APITokenServiceListResponse.result:type_name -> controlplane.v1.APITokenItem + 8, // 3: controlplane.v1.APITokenServiceCreateResponse.APITokenFull.item:type_name -> controlplane.v1.APITokenItem + 0, // 4: controlplane.v1.APITokenService.Create:input_type -> controlplane.v1.APITokenServiceCreateRequest + 4, // 5: controlplane.v1.APITokenService.List:input_type -> controlplane.v1.APITokenServiceListRequest + 2, // 6: controlplane.v1.APITokenService.Revoke:input_type -> controlplane.v1.APITokenServiceRevokeRequest + 1, // 7: controlplane.v1.APITokenService.Create:output_type -> controlplane.v1.APITokenServiceCreateResponse + 5, // 8: controlplane.v1.APITokenService.List:output_type -> controlplane.v1.APITokenServiceListResponse + 3, // 9: controlplane.v1.APITokenService.Revoke:output_type -> controlplane.v1.APITokenServiceRevokeResponse + 7, // [7:10] is the sub-list for method output_type + 4, // [4:7] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_controlplane_v1_api_token_proto_init() } @@ -647,6 +520,7 @@ func file_controlplane_v1_api_token_proto_init() { if File_controlplane_v1_api_token_proto != nil { return } + file_controlplane_v1_response_messages_proto_init() if !protoimpl.UnsafeEnabled { file_controlplane_v1_api_token_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*APITokenServiceCreateRequest); i { @@ -721,18 +595,6 @@ func file_controlplane_v1_api_token_proto_init() { } } file_controlplane_v1_api_token_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*APITokenItem); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controlplane_v1_api_token_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*APITokenServiceCreateResponse_APITokenFull); i { case 0: return &v.state @@ -752,7 +614,7 @@ func file_controlplane_v1_api_token_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_api_token_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/app/controlplane/api/controlplane/v1/api_token.proto b/app/controlplane/api/controlplane/v1/api_token.proto index 71a52f708..6bc5841cc 100644 --- a/app/controlplane/api/controlplane/v1/api_token.proto +++ b/app/controlplane/api/controlplane/v1/api_token.proto @@ -18,6 +18,7 @@ syntax = "proto3"; package controlplane.v1; import "buf/validate/validate.proto"; +import "controlplane/v1/response_messages.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; @@ -65,14 +66,3 @@ message APITokenServiceListRequest { message APITokenServiceListResponse { repeated APITokenItem result = 1; } - -message APITokenItem { - string id = 1; - string name = 7; - string description = 2; - string organization_id = 3; - string organization_name = 8; - google.protobuf.Timestamp created_at = 4; - google.protobuf.Timestamp revoked_at = 5; - google.protobuf.Timestamp expires_at = 6; -} diff --git a/app/controlplane/api/controlplane/v1/project.pb.go b/app/controlplane/api/controlplane/v1/project.pb.go new file mode 100644 index 000000000..020f7ecf1 --- /dev/null +++ b/app/controlplane/api/controlplane/v1/project.pb.go @@ -0,0 +1,647 @@ +// +// Copyright 2025 The Chainloop 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: controlplane/v1/project.proto + +package v1 + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ProjectServiceAPITokenCreateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + ProjectName string `protobuf:"bytes,2,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + ExpiresIn *durationpb.Duration `protobuf:"bytes,4,opt,name=expires_in,json=expiresIn,proto3,oneof" json:"expires_in,omitempty"` +} + +func (x *ProjectServiceAPITokenCreateRequest) Reset() { + *x = ProjectServiceAPITokenCreateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenCreateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenCreateRequest) ProtoMessage() {} + +func (x *ProjectServiceAPITokenCreateRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenCreateRequest.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenCreateRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{0} +} + +func (x *ProjectServiceAPITokenCreateRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProjectServiceAPITokenCreateRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *ProjectServiceAPITokenCreateRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *ProjectServiceAPITokenCreateRequest) GetExpiresIn() *durationpb.Duration { + if x != nil { + return x.ExpiresIn + } + return nil +} + +type ProjectServiceAPITokenCreateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result *ProjectServiceAPITokenCreateResponse_APITokenFull `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` +} + +func (x *ProjectServiceAPITokenCreateResponse) Reset() { + *x = ProjectServiceAPITokenCreateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenCreateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenCreateResponse) ProtoMessage() {} + +func (x *ProjectServiceAPITokenCreateResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenCreateResponse.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenCreateResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{1} +} + +func (x *ProjectServiceAPITokenCreateResponse) GetResult() *ProjectServiceAPITokenCreateResponse_APITokenFull { + if x != nil { + return x.Result + } + return nil +} + +type ProjectServiceAPITokenRevokeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *ProjectServiceAPITokenRevokeRequest) Reset() { + *x = ProjectServiceAPITokenRevokeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenRevokeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenRevokeRequest) ProtoMessage() {} + +func (x *ProjectServiceAPITokenRevokeRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenRevokeRequest.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenRevokeRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{2} +} + +func (x *ProjectServiceAPITokenRevokeRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type ProjectServiceAPITokenRevokeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ProjectServiceAPITokenRevokeResponse) Reset() { + *x = ProjectServiceAPITokenRevokeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenRevokeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenRevokeResponse) ProtoMessage() {} + +func (x *ProjectServiceAPITokenRevokeResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenRevokeResponse.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenRevokeResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{3} +} + +type ProjectServiceAPITokenListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IncludeRevoked bool `protobuf:"varint,1,opt,name=include_revoked,json=includeRevoked,proto3" json:"include_revoked,omitempty"` +} + +func (x *ProjectServiceAPITokenListRequest) Reset() { + *x = ProjectServiceAPITokenListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenListRequest) ProtoMessage() {} + +func (x *ProjectServiceAPITokenListRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenListRequest.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenListRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{4} +} + +func (x *ProjectServiceAPITokenListRequest) GetIncludeRevoked() bool { + if x != nil { + return x.IncludeRevoked + } + return false +} + +type ProjectServiceAPITokenListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result []*APITokenItem `protobuf:"bytes,1,rep,name=result,proto3" json:"result,omitempty"` +} + +func (x *ProjectServiceAPITokenListResponse) Reset() { + *x = ProjectServiceAPITokenListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenListResponse) ProtoMessage() {} + +func (x *ProjectServiceAPITokenListResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenListResponse.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenListResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{5} +} + +func (x *ProjectServiceAPITokenListResponse) GetResult() []*APITokenItem { + if x != nil { + return x.Result + } + return nil +} + +type ProjectServiceAPITokenCreateResponse_APITokenFull struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *APITokenItem `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + Jwt string `protobuf:"bytes,2,opt,name=jwt,proto3" json:"jwt,omitempty"` +} + +func (x *ProjectServiceAPITokenCreateResponse_APITokenFull) Reset() { + *x = ProjectServiceAPITokenCreateResponse_APITokenFull{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_project_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectServiceAPITokenCreateResponse_APITokenFull) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectServiceAPITokenCreateResponse_APITokenFull) ProtoMessage() {} + +func (x *ProjectServiceAPITokenCreateResponse_APITokenFull) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_project_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectServiceAPITokenCreateResponse_APITokenFull.ProtoReflect.Descriptor instead. +func (*ProjectServiceAPITokenCreateResponse_APITokenFull) Descriptor() ([]byte, []int) { + return file_controlplane_v1_project_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *ProjectServiceAPITokenCreateResponse_APITokenFull) GetItem() *APITokenItem { + if x != nil { + return x.Item + } + return nil +} + +func (x *ProjectServiceAPITokenCreateResponse_APITokenFull) GetJwt() string { + if x != nil { + return x.Jwt + } + return "" +} + +var File_controlplane_v1_project_proto protoreflect.FileDescriptor + +var file_controlplane_v1_project_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x01, 0x0a, 0x23, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, + 0x3d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, + 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, + 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x22, 0xd7, 0x01, + 0x0a, 0x24, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x50, + 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, + 0x6c, 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x23, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, + 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, + 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, + 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, + 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x4c, 0x0a, 0x21, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x5f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x22, + 0x5b, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x87, 0x03, 0x0a, + 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x7d, 0x0a, 0x0e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, + 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0e, 0x41, 0x50, 0x49, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, + 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, + 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controlplane_v1_project_proto_rawDescOnce sync.Once + file_controlplane_v1_project_proto_rawDescData = file_controlplane_v1_project_proto_rawDesc +) + +func file_controlplane_v1_project_proto_rawDescGZIP() []byte { + file_controlplane_v1_project_proto_rawDescOnce.Do(func() { + file_controlplane_v1_project_proto_rawDescData = protoimpl.X.CompressGZIP(file_controlplane_v1_project_proto_rawDescData) + }) + return file_controlplane_v1_project_proto_rawDescData +} + +var file_controlplane_v1_project_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_controlplane_v1_project_proto_goTypes = []interface{}{ + (*ProjectServiceAPITokenCreateRequest)(nil), // 0: controlplane.v1.ProjectServiceAPITokenCreateRequest + (*ProjectServiceAPITokenCreateResponse)(nil), // 1: controlplane.v1.ProjectServiceAPITokenCreateResponse + (*ProjectServiceAPITokenRevokeRequest)(nil), // 2: controlplane.v1.ProjectServiceAPITokenRevokeRequest + (*ProjectServiceAPITokenRevokeResponse)(nil), // 3: controlplane.v1.ProjectServiceAPITokenRevokeResponse + (*ProjectServiceAPITokenListRequest)(nil), // 4: controlplane.v1.ProjectServiceAPITokenListRequest + (*ProjectServiceAPITokenListResponse)(nil), // 5: controlplane.v1.ProjectServiceAPITokenListResponse + (*ProjectServiceAPITokenCreateResponse_APITokenFull)(nil), // 6: controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull + (*durationpb.Duration)(nil), // 7: google.protobuf.Duration + (*APITokenItem)(nil), // 8: controlplane.v1.APITokenItem +} +var file_controlplane_v1_project_proto_depIdxs = []int32{ + 7, // 0: controlplane.v1.ProjectServiceAPITokenCreateRequest.expires_in:type_name -> google.protobuf.Duration + 6, // 1: controlplane.v1.ProjectServiceAPITokenCreateResponse.result:type_name -> controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull + 8, // 2: controlplane.v1.ProjectServiceAPITokenListResponse.result:type_name -> controlplane.v1.APITokenItem + 8, // 3: controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.item:type_name -> controlplane.v1.APITokenItem + 0, // 4: controlplane.v1.ProjectService.APITokenCreate:input_type -> controlplane.v1.ProjectServiceAPITokenCreateRequest + 4, // 5: controlplane.v1.ProjectService.APITokenList:input_type -> controlplane.v1.ProjectServiceAPITokenListRequest + 2, // 6: controlplane.v1.ProjectService.APITokenRevoke:input_type -> controlplane.v1.ProjectServiceAPITokenRevokeRequest + 1, // 7: controlplane.v1.ProjectService.APITokenCreate:output_type -> controlplane.v1.ProjectServiceAPITokenCreateResponse + 5, // 8: controlplane.v1.ProjectService.APITokenList:output_type -> controlplane.v1.ProjectServiceAPITokenListResponse + 3, // 9: controlplane.v1.ProjectService.APITokenRevoke:output_type -> controlplane.v1.ProjectServiceAPITokenRevokeResponse + 7, // [7:10] is the sub-list for method output_type + 4, // [4:7] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_controlplane_v1_project_proto_init() } +func file_controlplane_v1_project_proto_init() { + if File_controlplane_v1_project_proto != nil { + return + } + file_controlplane_v1_response_messages_proto_init() + if !protoimpl.UnsafeEnabled { + file_controlplane_v1_project_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenCreateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_project_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenCreateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_project_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenRevokeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_project_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenRevokeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_project_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_project_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_project_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectServiceAPITokenCreateResponse_APITokenFull); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_controlplane_v1_project_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controlplane_v1_project_proto_rawDesc, + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_controlplane_v1_project_proto_goTypes, + DependencyIndexes: file_controlplane_v1_project_proto_depIdxs, + MessageInfos: file_controlplane_v1_project_proto_msgTypes, + }.Build() + File_controlplane_v1_project_proto = out.File + file_controlplane_v1_project_proto_rawDesc = nil + file_controlplane_v1_project_proto_goTypes = nil + file_controlplane_v1_project_proto_depIdxs = nil +} diff --git a/app/controlplane/api/controlplane/v1/project.proto b/app/controlplane/api/controlplane/v1/project.proto new file mode 100644 index 000000000..5ef2b3e34 --- /dev/null +++ b/app/controlplane/api/controlplane/v1/project.proto @@ -0,0 +1,68 @@ +// +// Copyright 2025 The Chainloop 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 controlplane.v1; + +import "buf/validate/validate.proto"; +import "controlplane/v1/response_messages.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1;v1"; + +service ProjectService { + // Project level API tokens + rpc APITokenCreate(ProjectServiceAPITokenCreateRequest) returns (ProjectServiceAPITokenCreateResponse); + rpc APITokenList(ProjectServiceAPITokenListRequest) returns (ProjectServiceAPITokenListResponse); + rpc APITokenRevoke(ProjectServiceAPITokenRevokeRequest) returns (ProjectServiceAPITokenRevokeResponse); +} + +message ProjectServiceAPITokenCreateRequest { + string name = 1 [(buf.validate.field).string.min_len = 1]; + string project_name = 2 [(buf.validate.field).string.min_len = 1]; + optional string description = 3; + optional google.protobuf.Duration expires_in = 4; +} + +message ProjectServiceAPITokenCreateResponse { + APITokenFull result = 1; + + message APITokenFull { + APITokenItem item = 1; + string jwt = 2; + } +} + +message ProjectServiceAPITokenRevokeRequest { + string name = 1 [(buf.validate.field) = { + // NOTE: validations can not be shared yet https://github.com/bufbuild/protovalidate/issues/51 + cel: { + message: "must contain only lowercase letters, numbers, and hyphens." + expression: "this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')" + id: "name.dns-1123" + } + }]; +} + +message ProjectServiceAPITokenRevokeResponse {} + +message ProjectServiceAPITokenListRequest { + bool include_revoked = 1; +} + +message ProjectServiceAPITokenListResponse { + repeated APITokenItem result = 1; +} diff --git a/app/controlplane/api/controlplane/v1/project_grpc.pb.go b/app/controlplane/api/controlplane/v1/project_grpc.pb.go new file mode 100644 index 000000000..98aff31ce --- /dev/null +++ b/app/controlplane/api/controlplane/v1/project_grpc.pb.go @@ -0,0 +1,200 @@ +// +// Copyright 2025 The Chainloop 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. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: controlplane/v1/project.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ProjectService_APITokenCreate_FullMethodName = "/controlplane.v1.ProjectService/APITokenCreate" + ProjectService_APITokenList_FullMethodName = "/controlplane.v1.ProjectService/APITokenList" + ProjectService_APITokenRevoke_FullMethodName = "/controlplane.v1.ProjectService/APITokenRevoke" +) + +// ProjectServiceClient is the client API for ProjectService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ProjectServiceClient interface { + // Project level API tokens + APITokenCreate(ctx context.Context, in *ProjectServiceAPITokenCreateRequest, opts ...grpc.CallOption) (*ProjectServiceAPITokenCreateResponse, error) + APITokenList(ctx context.Context, in *ProjectServiceAPITokenListRequest, opts ...grpc.CallOption) (*ProjectServiceAPITokenListResponse, error) + APITokenRevoke(ctx context.Context, in *ProjectServiceAPITokenRevokeRequest, opts ...grpc.CallOption) (*ProjectServiceAPITokenRevokeResponse, error) +} + +type projectServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewProjectServiceClient(cc grpc.ClientConnInterface) ProjectServiceClient { + return &projectServiceClient{cc} +} + +func (c *projectServiceClient) APITokenCreate(ctx context.Context, in *ProjectServiceAPITokenCreateRequest, opts ...grpc.CallOption) (*ProjectServiceAPITokenCreateResponse, error) { + out := new(ProjectServiceAPITokenCreateResponse) + err := c.cc.Invoke(ctx, ProjectService_APITokenCreate_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *projectServiceClient) APITokenList(ctx context.Context, in *ProjectServiceAPITokenListRequest, opts ...grpc.CallOption) (*ProjectServiceAPITokenListResponse, error) { + out := new(ProjectServiceAPITokenListResponse) + err := c.cc.Invoke(ctx, ProjectService_APITokenList_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *projectServiceClient) APITokenRevoke(ctx context.Context, in *ProjectServiceAPITokenRevokeRequest, opts ...grpc.CallOption) (*ProjectServiceAPITokenRevokeResponse, error) { + out := new(ProjectServiceAPITokenRevokeResponse) + err := c.cc.Invoke(ctx, ProjectService_APITokenRevoke_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ProjectServiceServer is the server API for ProjectService service. +// All implementations must embed UnimplementedProjectServiceServer +// for forward compatibility +type ProjectServiceServer interface { + // Project level API tokens + APITokenCreate(context.Context, *ProjectServiceAPITokenCreateRequest) (*ProjectServiceAPITokenCreateResponse, error) + APITokenList(context.Context, *ProjectServiceAPITokenListRequest) (*ProjectServiceAPITokenListResponse, error) + APITokenRevoke(context.Context, *ProjectServiceAPITokenRevokeRequest) (*ProjectServiceAPITokenRevokeResponse, error) + mustEmbedUnimplementedProjectServiceServer() +} + +// UnimplementedProjectServiceServer must be embedded to have forward compatible implementations. +type UnimplementedProjectServiceServer struct { +} + +func (UnimplementedProjectServiceServer) APITokenCreate(context.Context, *ProjectServiceAPITokenCreateRequest) (*ProjectServiceAPITokenCreateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method APITokenCreate not implemented") +} +func (UnimplementedProjectServiceServer) APITokenList(context.Context, *ProjectServiceAPITokenListRequest) (*ProjectServiceAPITokenListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method APITokenList not implemented") +} +func (UnimplementedProjectServiceServer) APITokenRevoke(context.Context, *ProjectServiceAPITokenRevokeRequest) (*ProjectServiceAPITokenRevokeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method APITokenRevoke not implemented") +} +func (UnimplementedProjectServiceServer) mustEmbedUnimplementedProjectServiceServer() {} + +// UnsafeProjectServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ProjectServiceServer will +// result in compilation errors. +type UnsafeProjectServiceServer interface { + mustEmbedUnimplementedProjectServiceServer() +} + +func RegisterProjectServiceServer(s grpc.ServiceRegistrar, srv ProjectServiceServer) { + s.RegisterService(&ProjectService_ServiceDesc, srv) +} + +func _ProjectService_APITokenCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ProjectServiceAPITokenCreateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProjectServiceServer).APITokenCreate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProjectService_APITokenCreate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProjectServiceServer).APITokenCreate(ctx, req.(*ProjectServiceAPITokenCreateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProjectService_APITokenList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ProjectServiceAPITokenListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProjectServiceServer).APITokenList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProjectService_APITokenList_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProjectServiceServer).APITokenList(ctx, req.(*ProjectServiceAPITokenListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProjectService_APITokenRevoke_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ProjectServiceAPITokenRevokeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProjectServiceServer).APITokenRevoke(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProjectService_APITokenRevoke_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProjectServiceServer).APITokenRevoke(ctx, req.(*ProjectServiceAPITokenRevokeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ProjectService_ServiceDesc is the grpc.ServiceDesc for ProjectService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ProjectService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "controlplane.v1.ProjectService", + HandlerType: (*ProjectServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "APITokenCreate", + Handler: _ProjectService_APITokenCreate_Handler, + }, + { + MethodName: "APITokenList", + Handler: _ProjectService_APITokenList_Handler, + }, + { + MethodName: "APITokenRevoke", + Handler: _ProjectService_APITokenRevoke_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "controlplane/v1/project.proto", +} diff --git a/app/controlplane/api/controlplane/v1/response_messages.pb.go b/app/controlplane/api/controlplane/v1/response_messages.pb.go index 657014923..dea53eeb4 100644 --- a/app/controlplane/api/controlplane/v1/response_messages.pb.go +++ b/app/controlplane/api/controlplane/v1/response_messages.pb.go @@ -2028,6 +2028,125 @@ func (*EntityRef_EntityId) isEntityRef_Ref() {} func (*EntityRef_EntityName) isEntityRef_Ref() {} +type APITokenItem struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + OrganizationId string `protobuf:"bytes,3,opt,name=organization_id,json=organizationId,proto3" json:"organization_id,omitempty"` + OrganizationName string `protobuf:"bytes,8,opt,name=organization_name,json=organizationName,proto3" json:"organization_name,omitempty"` + ProjectId string `protobuf:"bytes,9,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` + ProjectName string `protobuf:"bytes,10,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + RevokedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=revoked_at,json=revokedAt,proto3" json:"revoked_at,omitempty"` + ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` +} + +func (x *APITokenItem) Reset() { + *x = APITokenItem{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_response_messages_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *APITokenItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*APITokenItem) ProtoMessage() {} + +func (x *APITokenItem) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_response_messages_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use APITokenItem.ProtoReflect.Descriptor instead. +func (*APITokenItem) Descriptor() ([]byte, []int) { + return file_controlplane_v1_response_messages_proto_rawDescGZIP(), []int{16} +} + +func (x *APITokenItem) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *APITokenItem) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *APITokenItem) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *APITokenItem) GetOrganizationId() string { + if x != nil { + return x.OrganizationId + } + return "" +} + +func (x *APITokenItem) GetOrganizationName() string { + if x != nil { + return x.OrganizationName + } + return "" +} + +func (x *APITokenItem) GetProjectId() string { + if x != nil { + return x.ProjectId + } + return "" +} + +func (x *APITokenItem) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *APITokenItem) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *APITokenItem) GetRevokedAt() *timestamppb.Timestamp { + if x != nil { + return x.RevokedAt + } + return nil +} + +func (x *APITokenItem) GetExpiresAt() *timestamppb.Timestamp { + if x != nil { + return x.ExpiresAt + } + return nil +} + type AttestationItem_PolicyEvaluationStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2042,7 +2161,7 @@ type AttestationItem_PolicyEvaluationStatus struct { func (x *AttestationItem_PolicyEvaluationStatus) Reset() { *x = AttestationItem_PolicyEvaluationStatus{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[18] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2055,7 +2174,7 @@ func (x *AttestationItem_PolicyEvaluationStatus) String() string { func (*AttestationItem_PolicyEvaluationStatus) ProtoMessage() {} func (x *AttestationItem_PolicyEvaluationStatus) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[18] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2111,7 +2230,7 @@ type AttestationItem_EnvVariable struct { func (x *AttestationItem_EnvVariable) Reset() { *x = AttestationItem_EnvVariable{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[19] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2124,7 +2243,7 @@ func (x *AttestationItem_EnvVariable) String() string { func (*AttestationItem_EnvVariable) ProtoMessage() {} func (x *AttestationItem_EnvVariable) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[19] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2179,7 +2298,7 @@ type AttestationItem_Material struct { func (x *AttestationItem_Material) Reset() { *x = AttestationItem_Material{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[20] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2192,7 +2311,7 @@ func (x *AttestationItem_Material) String() string { func (*AttestationItem_Material) ProtoMessage() {} func (x *AttestationItem_Material) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[20] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2283,7 +2402,7 @@ type WorkflowContractVersionItem_RawBody struct { func (x *WorkflowContractVersionItem_RawBody) Reset() { *x = WorkflowContractVersionItem_RawBody{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[25] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2296,7 +2415,7 @@ func (x *WorkflowContractVersionItem_RawBody) String() string { func (*WorkflowContractVersionItem_RawBody) ProtoMessage() {} func (x *WorkflowContractVersionItem_RawBody) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[25] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2338,7 +2457,7 @@ type CASBackendItem_Limits struct { func (x *CASBackendItem_Limits) Reset() { *x = CASBackendItem_Limits{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[26] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2351,7 +2470,7 @@ func (x *CASBackendItem_Limits) String() string { func (*CASBackendItem_Limits) ProtoMessage() {} func (x *CASBackendItem_Limits) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_v1_response_messages_proto_msgTypes[26] + mi := &file_controlplane_v1_response_messages_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2809,7 +2928,33 @@ var file_controlplane_v1_response_messages_proto_rawDesc = []byte{ 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, - 0x61, 0x6d, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x2a, 0xa6, 0x01, 0x0a, 0x09, 0x52, + 0x61, 0x6d, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x22, 0x9d, 0x03, 0x0a, 0x0c, 0x41, + 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x67, 0x61, + 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x2a, 0xa6, 0x01, 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x55, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x55, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, @@ -2882,7 +3027,7 @@ func file_controlplane_v1_response_messages_proto_rawDescGZIP() []byte { } var file_controlplane_v1_response_messages_proto_enumTypes = make([]protoimpl.EnumInfo, 9) -var file_controlplane_v1_response_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_controlplane_v1_response_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 28) var file_controlplane_v1_response_messages_proto_goTypes = []interface{}{ (RunStatus)(0), // 0: controlplane.v1.RunStatus (MembershipRole)(0), // 1: controlplane.v1.MembershipRole @@ -2909,71 +3054,75 @@ var file_controlplane_v1_response_messages_proto_goTypes = []interface{}{ (*OrgItem)(nil), // 22: controlplane.v1.OrgItem (*CASBackendItem)(nil), // 23: controlplane.v1.CASBackendItem (*EntityRef)(nil), // 24: controlplane.v1.EntityRef - nil, // 25: controlplane.v1.AttestationItem.AnnotationsEntry - nil, // 26: controlplane.v1.AttestationItem.PolicyEvaluationsEntry - (*AttestationItem_PolicyEvaluationStatus)(nil), // 27: controlplane.v1.AttestationItem.PolicyEvaluationStatus - (*AttestationItem_EnvVariable)(nil), // 28: controlplane.v1.AttestationItem.EnvVariable - (*AttestationItem_Material)(nil), // 29: controlplane.v1.AttestationItem.Material - nil, // 30: controlplane.v1.AttestationItem.Material.AnnotationsEntry - nil, // 31: controlplane.v1.PolicyEvaluation.AnnotationsEntry - nil, // 32: controlplane.v1.PolicyEvaluation.WithEntry - nil, // 33: controlplane.v1.PolicyReference.DigestEntry - (*WorkflowContractVersionItem_RawBody)(nil), // 34: controlplane.v1.WorkflowContractVersionItem.RawBody - (*CASBackendItem_Limits)(nil), // 35: controlplane.v1.CASBackendItem.Limits - (*timestamppb.Timestamp)(nil), // 36: google.protobuf.Timestamp - (v1.CraftingSchema_Runner_RunnerType)(0), // 37: workflowcontract.v1.CraftingSchema.Runner.RunnerType - (*v1.CraftingSchema)(nil), // 38: workflowcontract.v1.CraftingSchema + (*APITokenItem)(nil), // 25: controlplane.v1.APITokenItem + nil, // 26: controlplane.v1.AttestationItem.AnnotationsEntry + nil, // 27: controlplane.v1.AttestationItem.PolicyEvaluationsEntry + (*AttestationItem_PolicyEvaluationStatus)(nil), // 28: controlplane.v1.AttestationItem.PolicyEvaluationStatus + (*AttestationItem_EnvVariable)(nil), // 29: controlplane.v1.AttestationItem.EnvVariable + (*AttestationItem_Material)(nil), // 30: controlplane.v1.AttestationItem.Material + nil, // 31: controlplane.v1.AttestationItem.Material.AnnotationsEntry + nil, // 32: controlplane.v1.PolicyEvaluation.AnnotationsEntry + nil, // 33: controlplane.v1.PolicyEvaluation.WithEntry + nil, // 34: controlplane.v1.PolicyReference.DigestEntry + (*WorkflowContractVersionItem_RawBody)(nil), // 35: controlplane.v1.WorkflowContractVersionItem.RawBody + (*CASBackendItem_Limits)(nil), // 36: controlplane.v1.CASBackendItem.Limits + (*timestamppb.Timestamp)(nil), // 37: google.protobuf.Timestamp + (v1.CraftingSchema_Runner_RunnerType)(0), // 38: workflowcontract.v1.CraftingSchema.Runner.RunnerType + (*v1.CraftingSchema)(nil), // 39: workflowcontract.v1.CraftingSchema } var file_controlplane_v1_response_messages_proto_depIdxs = []int32{ - 36, // 0: controlplane.v1.WorkflowItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 0: controlplane.v1.WorkflowItem.created_at:type_name -> google.protobuf.Timestamp 10, // 1: controlplane.v1.WorkflowItem.last_run:type_name -> controlplane.v1.WorkflowRunItem - 36, // 2: controlplane.v1.WorkflowRunItem.created_at:type_name -> google.protobuf.Timestamp - 36, // 3: controlplane.v1.WorkflowRunItem.finished_at:type_name -> google.protobuf.Timestamp + 37, // 2: controlplane.v1.WorkflowRunItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 3: controlplane.v1.WorkflowRunItem.finished_at:type_name -> google.protobuf.Timestamp 0, // 4: controlplane.v1.WorkflowRunItem.status:type_name -> controlplane.v1.RunStatus 9, // 5: controlplane.v1.WorkflowRunItem.workflow:type_name -> controlplane.v1.WorkflowItem - 37, // 6: controlplane.v1.WorkflowRunItem.runner_type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType + 38, // 6: controlplane.v1.WorkflowRunItem.runner_type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType 19, // 7: controlplane.v1.WorkflowRunItem.contract_version:type_name -> controlplane.v1.WorkflowContractVersionItem 11, // 8: controlplane.v1.WorkflowRunItem.version:type_name -> controlplane.v1.ProjectVersion - 36, // 9: controlplane.v1.ProjectVersion.created_at:type_name -> google.protobuf.Timestamp - 36, // 10: controlplane.v1.ProjectVersion.released_at:type_name -> google.protobuf.Timestamp - 28, // 11: controlplane.v1.AttestationItem.env_vars:type_name -> controlplane.v1.AttestationItem.EnvVariable - 29, // 12: controlplane.v1.AttestationItem.materials:type_name -> controlplane.v1.AttestationItem.Material - 25, // 13: controlplane.v1.AttestationItem.annotations:type_name -> controlplane.v1.AttestationItem.AnnotationsEntry - 26, // 14: controlplane.v1.AttestationItem.policy_evaluations:type_name -> controlplane.v1.AttestationItem.PolicyEvaluationsEntry - 27, // 15: controlplane.v1.AttestationItem.policy_evaluation_status:type_name -> controlplane.v1.AttestationItem.PolicyEvaluationStatus + 37, // 9: controlplane.v1.ProjectVersion.created_at:type_name -> google.protobuf.Timestamp + 37, // 10: controlplane.v1.ProjectVersion.released_at:type_name -> google.protobuf.Timestamp + 29, // 11: controlplane.v1.AttestationItem.env_vars:type_name -> controlplane.v1.AttestationItem.EnvVariable + 30, // 12: controlplane.v1.AttestationItem.materials:type_name -> controlplane.v1.AttestationItem.Material + 26, // 13: controlplane.v1.AttestationItem.annotations:type_name -> controlplane.v1.AttestationItem.AnnotationsEntry + 27, // 14: controlplane.v1.AttestationItem.policy_evaluations:type_name -> controlplane.v1.AttestationItem.PolicyEvaluationsEntry + 28, // 15: controlplane.v1.AttestationItem.policy_evaluation_status:type_name -> controlplane.v1.AttestationItem.PolicyEvaluationStatus 14, // 16: controlplane.v1.PolicyEvaluations.evaluations:type_name -> controlplane.v1.PolicyEvaluation - 31, // 17: controlplane.v1.PolicyEvaluation.annotations:type_name -> controlplane.v1.PolicyEvaluation.AnnotationsEntry - 32, // 18: controlplane.v1.PolicyEvaluation.with:type_name -> controlplane.v1.PolicyEvaluation.WithEntry + 32, // 17: controlplane.v1.PolicyEvaluation.annotations:type_name -> controlplane.v1.PolicyEvaluation.AnnotationsEntry + 33, // 18: controlplane.v1.PolicyEvaluation.with:type_name -> controlplane.v1.PolicyEvaluation.WithEntry 15, // 19: controlplane.v1.PolicyEvaluation.violations:type_name -> controlplane.v1.PolicyViolation 16, // 20: controlplane.v1.PolicyEvaluation.policy_reference:type_name -> controlplane.v1.PolicyReference 16, // 21: controlplane.v1.PolicyEvaluation.group_reference:type_name -> controlplane.v1.PolicyReference - 33, // 22: controlplane.v1.PolicyReference.digest:type_name -> controlplane.v1.PolicyReference.DigestEntry - 36, // 23: controlplane.v1.WorkflowContractItem.created_at:type_name -> google.protobuf.Timestamp - 36, // 24: controlplane.v1.WorkflowContractItem.latest_revision_created_at:type_name -> google.protobuf.Timestamp + 34, // 22: controlplane.v1.PolicyReference.digest:type_name -> controlplane.v1.PolicyReference.DigestEntry + 37, // 23: controlplane.v1.WorkflowContractItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 24: controlplane.v1.WorkflowContractItem.latest_revision_created_at:type_name -> google.protobuf.Timestamp 18, // 25: controlplane.v1.WorkflowContractItem.workflow_refs:type_name -> controlplane.v1.WorkflowRef - 36, // 26: controlplane.v1.WorkflowContractVersionItem.created_at:type_name -> google.protobuf.Timestamp - 38, // 27: controlplane.v1.WorkflowContractVersionItem.v1:type_name -> workflowcontract.v1.CraftingSchema - 34, // 28: controlplane.v1.WorkflowContractVersionItem.raw_contract:type_name -> controlplane.v1.WorkflowContractVersionItem.RawBody - 36, // 29: controlplane.v1.User.created_at:type_name -> google.protobuf.Timestamp + 37, // 26: controlplane.v1.WorkflowContractVersionItem.created_at:type_name -> google.protobuf.Timestamp + 39, // 27: controlplane.v1.WorkflowContractVersionItem.v1:type_name -> workflowcontract.v1.CraftingSchema + 35, // 28: controlplane.v1.WorkflowContractVersionItem.raw_contract:type_name -> controlplane.v1.WorkflowContractVersionItem.RawBody + 37, // 29: controlplane.v1.User.created_at:type_name -> google.protobuf.Timestamp 22, // 30: controlplane.v1.OrgMembershipItem.org:type_name -> controlplane.v1.OrgItem 20, // 31: controlplane.v1.OrgMembershipItem.user:type_name -> controlplane.v1.User - 36, // 32: controlplane.v1.OrgMembershipItem.created_at:type_name -> google.protobuf.Timestamp - 36, // 33: controlplane.v1.OrgMembershipItem.updated_at:type_name -> google.protobuf.Timestamp + 37, // 32: controlplane.v1.OrgMembershipItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 33: controlplane.v1.OrgMembershipItem.updated_at:type_name -> google.protobuf.Timestamp 1, // 34: controlplane.v1.OrgMembershipItem.role:type_name -> controlplane.v1.MembershipRole - 36, // 35: controlplane.v1.OrgItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 35: controlplane.v1.OrgItem.created_at:type_name -> google.protobuf.Timestamp 7, // 36: controlplane.v1.OrgItem.default_policy_violation_strategy:type_name -> controlplane.v1.OrgItem.PolicyViolationBlockingStrategy - 36, // 37: controlplane.v1.CASBackendItem.created_at:type_name -> google.protobuf.Timestamp - 36, // 38: controlplane.v1.CASBackendItem.validated_at:type_name -> google.protobuf.Timestamp + 37, // 37: controlplane.v1.CASBackendItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 38: controlplane.v1.CASBackendItem.validated_at:type_name -> google.protobuf.Timestamp 8, // 39: controlplane.v1.CASBackendItem.validation_status:type_name -> controlplane.v1.CASBackendItem.ValidationStatus - 35, // 40: controlplane.v1.CASBackendItem.limits:type_name -> controlplane.v1.CASBackendItem.Limits - 13, // 41: controlplane.v1.AttestationItem.PolicyEvaluationsEntry.value:type_name -> controlplane.v1.PolicyEvaluations - 30, // 42: controlplane.v1.AttestationItem.Material.annotations:type_name -> controlplane.v1.AttestationItem.Material.AnnotationsEntry - 6, // 43: controlplane.v1.WorkflowContractVersionItem.RawBody.format:type_name -> controlplane.v1.WorkflowContractVersionItem.RawBody.Format - 44, // [44:44] is the sub-list for method output_type - 44, // [44:44] is the sub-list for method input_type - 44, // [44:44] is the sub-list for extension type_name - 44, // [44:44] is the sub-list for extension extendee - 0, // [0:44] is the sub-list for field type_name + 36, // 40: controlplane.v1.CASBackendItem.limits:type_name -> controlplane.v1.CASBackendItem.Limits + 37, // 41: controlplane.v1.APITokenItem.created_at:type_name -> google.protobuf.Timestamp + 37, // 42: controlplane.v1.APITokenItem.revoked_at:type_name -> google.protobuf.Timestamp + 37, // 43: controlplane.v1.APITokenItem.expires_at:type_name -> google.protobuf.Timestamp + 13, // 44: controlplane.v1.AttestationItem.PolicyEvaluationsEntry.value:type_name -> controlplane.v1.PolicyEvaluations + 31, // 45: controlplane.v1.AttestationItem.Material.annotations:type_name -> controlplane.v1.AttestationItem.Material.AnnotationsEntry + 6, // 46: controlplane.v1.WorkflowContractVersionItem.RawBody.format:type_name -> controlplane.v1.WorkflowContractVersionItem.RawBody.Format + 47, // [47:47] is the sub-list for method output_type + 47, // [47:47] is the sub-list for method input_type + 47, // [47:47] is the sub-list for extension type_name + 47, // [47:47] is the sub-list for extension extendee + 0, // [0:47] is the sub-list for field type_name } func init() { file_controlplane_v1_response_messages_proto_init() } @@ -3174,8 +3323,8 @@ func file_controlplane_v1_response_messages_proto_init() { return nil } } - file_controlplane_v1_response_messages_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AttestationItem_PolicyEvaluationStatus); i { + file_controlplane_v1_response_messages_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*APITokenItem); i { case 0: return &v.state case 1: @@ -3187,7 +3336,7 @@ func file_controlplane_v1_response_messages_proto_init() { } } file_controlplane_v1_response_messages_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AttestationItem_EnvVariable); i { + switch v := v.(*AttestationItem_PolicyEvaluationStatus); i { case 0: return &v.state case 1: @@ -3199,6 +3348,18 @@ func file_controlplane_v1_response_messages_proto_init() { } } file_controlplane_v1_response_messages_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttestationItem_EnvVariable); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_response_messages_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AttestationItem_Material); i { case 0: return &v.state @@ -3210,7 +3371,7 @@ func file_controlplane_v1_response_messages_proto_init() { return nil } } - file_controlplane_v1_response_messages_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_controlplane_v1_response_messages_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowContractVersionItem_RawBody); i { case 0: return &v.state @@ -3222,7 +3383,7 @@ func file_controlplane_v1_response_messages_proto_init() { return nil } } - file_controlplane_v1_response_messages_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_controlplane_v1_response_messages_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CASBackendItem_Limits); i { case 0: return &v.state @@ -3248,7 +3409,7 @@ func file_controlplane_v1_response_messages_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_response_messages_proto_rawDesc, NumEnums: 9, - NumMessages: 27, + NumMessages: 28, NumExtensions: 0, NumServices: 0, }, diff --git a/app/controlplane/api/controlplane/v1/response_messages.proto b/app/controlplane/api/controlplane/v1/response_messages.proto index 93d179e58..c4d8d25ab 100644 --- a/app/controlplane/api/controlplane/v1/response_messages.proto +++ b/app/controlplane/api/controlplane/v1/response_messages.proto @@ -326,3 +326,16 @@ message EntityRef { }]; } } + +message APITokenItem { + string id = 1; + string name = 7; + string description = 2; + string organization_id = 3; + string organization_name = 8; + string project_id = 9; + string project_name = 10; + google.protobuf.Timestamp created_at = 4; + google.protobuf.Timestamp revoked_at = 5; + google.protobuf.Timestamp expires_at = 6; +} diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/api_token.ts b/app/controlplane/api/gen/frontend/controlplane/v1/api_token.ts index 8d1702e92..6fd14efee 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/api_token.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/api_token.ts @@ -3,7 +3,7 @@ import { grpc } from "@improbable-eng/grpc-web"; import { BrowserHeaders } from "browser-headers"; import _m0 from "protobufjs/minimal"; import { Duration } from "../../google/protobuf/duration"; -import { Timestamp } from "../../google/protobuf/timestamp"; +import { APITokenItem } from "./response_messages"; export const protobufPackage = "controlplane.v1"; @@ -37,17 +37,6 @@ export interface APITokenServiceListResponse { result: APITokenItem[]; } -export interface APITokenItem { - id: string; - name: string; - description: string; - organizationId: string; - organizationName: string; - createdAt?: Date; - revokedAt?: Date; - expiresAt?: Date; -} - function createBaseAPITokenServiceCreateRequest(): APITokenServiceCreateRequest { return { name: "", description: undefined, expiresIn: undefined }; } @@ -491,164 +480,6 @@ export const APITokenServiceListResponse = { }, }; -function createBaseAPITokenItem(): APITokenItem { - return { - id: "", - name: "", - description: "", - organizationId: "", - organizationName: "", - createdAt: undefined, - revokedAt: undefined, - expiresAt: undefined, - }; -} - -export const APITokenItem = { - encode(message: APITokenItem, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.id !== "") { - writer.uint32(10).string(message.id); - } - if (message.name !== "") { - writer.uint32(58).string(message.name); - } - if (message.description !== "") { - writer.uint32(18).string(message.description); - } - if (message.organizationId !== "") { - writer.uint32(26).string(message.organizationId); - } - if (message.organizationName !== "") { - writer.uint32(66).string(message.organizationName); - } - if (message.createdAt !== undefined) { - Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(34).fork()).ldelim(); - } - if (message.revokedAt !== undefined) { - Timestamp.encode(toTimestamp(message.revokedAt), writer.uint32(42).fork()).ldelim(); - } - if (message.expiresAt !== undefined) { - Timestamp.encode(toTimestamp(message.expiresAt), writer.uint32(50).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): APITokenItem { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseAPITokenItem(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.id = reader.string(); - continue; - case 7: - if (tag !== 58) { - break; - } - - message.name = reader.string(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.description = reader.string(); - continue; - case 3: - if (tag !== 26) { - break; - } - - message.organizationId = reader.string(); - continue; - case 8: - if (tag !== 66) { - break; - } - - message.organizationName = reader.string(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.revokedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); - continue; - case 6: - if (tag !== 50) { - break; - } - - message.expiresAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): APITokenItem { - return { - id: isSet(object.id) ? String(object.id) : "", - name: isSet(object.name) ? String(object.name) : "", - description: isSet(object.description) ? String(object.description) : "", - organizationId: isSet(object.organizationId) ? String(object.organizationId) : "", - organizationName: isSet(object.organizationName) ? String(object.organizationName) : "", - createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, - revokedAt: isSet(object.revokedAt) ? fromJsonTimestamp(object.revokedAt) : undefined, - expiresAt: isSet(object.expiresAt) ? fromJsonTimestamp(object.expiresAt) : undefined, - }; - }, - - toJSON(message: APITokenItem): unknown { - const obj: any = {}; - message.id !== undefined && (obj.id = message.id); - message.name !== undefined && (obj.name = message.name); - message.description !== undefined && (obj.description = message.description); - message.organizationId !== undefined && (obj.organizationId = message.organizationId); - message.organizationName !== undefined && (obj.organizationName = message.organizationName); - message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); - message.revokedAt !== undefined && (obj.revokedAt = message.revokedAt.toISOString()); - message.expiresAt !== undefined && (obj.expiresAt = message.expiresAt.toISOString()); - return obj; - }, - - create, I>>(base?: I): APITokenItem { - return APITokenItem.fromPartial(base ?? {}); - }, - - fromPartial, I>>(object: I): APITokenItem { - const message = createBaseAPITokenItem(); - message.id = object.id ?? ""; - message.name = object.name ?? ""; - message.description = object.description ?? ""; - message.organizationId = object.organizationId ?? ""; - message.organizationName = object.organizationName ?? ""; - message.createdAt = object.createdAt ?? undefined; - message.revokedAt = object.revokedAt ?? undefined; - message.expiresAt = object.expiresAt ?? undefined; - return message; - }, -}; - export interface APITokenService { Create( request: DeepPartial, @@ -865,28 +696,6 @@ type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin ? P : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; -function toTimestamp(date: Date): Timestamp { - const seconds = date.getTime() / 1_000; - const nanos = (date.getTime() % 1_000) * 1_000_000; - return { seconds, nanos }; -} - -function fromTimestamp(t: Timestamp): Date { - let millis = (t.seconds || 0) * 1_000; - millis += (t.nanos || 0) / 1_000_000; - return new Date(millis); -} - -function fromJsonTimestamp(o: any): Date { - if (o instanceof Date) { - return o; - } else if (typeof o === "string") { - return new Date(o); - } else { - return fromTimestamp(Timestamp.fromJSON(o)); - } -} - function isSet(value: any): boolean { return value !== null && value !== undefined; } diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts new file mode 100644 index 000000000..a8aa239b4 --- /dev/null +++ b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts @@ -0,0 +1,762 @@ +/* eslint-disable */ +import { grpc } from "@improbable-eng/grpc-web"; +import { BrowserHeaders } from "browser-headers"; +import _m0 from "protobufjs/minimal"; +import { Duration } from "../../google/protobuf/duration"; +import { APITokenItem } from "./response_messages"; + +export const protobufPackage = "controlplane.v1"; + +export interface ProjectServiceAPITokenCreateRequest { + name: string; + projectName: string; + description?: string | undefined; + expiresIn?: Duration | undefined; +} + +export interface ProjectServiceAPITokenCreateResponse { + result?: ProjectServiceAPITokenCreateResponse_APITokenFull; +} + +export interface ProjectServiceAPITokenCreateResponse_APITokenFull { + item?: APITokenItem; + jwt: string; +} + +export interface ProjectServiceAPITokenRevokeRequest { + name: string; +} + +export interface ProjectServiceAPITokenRevokeResponse { +} + +export interface ProjectServiceAPITokenListRequest { + includeRevoked: boolean; +} + +export interface ProjectServiceAPITokenListResponse { + result: APITokenItem[]; +} + +function createBaseProjectServiceAPITokenCreateRequest(): ProjectServiceAPITokenCreateRequest { + return { name: "", projectName: "", description: undefined, expiresIn: undefined }; +} + +export const ProjectServiceAPITokenCreateRequest = { + encode(message: ProjectServiceAPITokenCreateRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.projectName !== "") { + writer.uint32(18).string(message.projectName); + } + if (message.description !== undefined) { + writer.uint32(26).string(message.description); + } + if (message.expiresIn !== undefined) { + Duration.encode(message.expiresIn, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenCreateRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenCreateRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.projectName = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.description = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.expiresIn = Duration.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectServiceAPITokenCreateRequest { + return { + name: isSet(object.name) ? String(object.name) : "", + projectName: isSet(object.projectName) ? String(object.projectName) : "", + description: isSet(object.description) ? String(object.description) : undefined, + expiresIn: isSet(object.expiresIn) ? Duration.fromJSON(object.expiresIn) : undefined, + }; + }, + + toJSON(message: ProjectServiceAPITokenCreateRequest): unknown { + const obj: any = {}; + message.name !== undefined && (obj.name = message.name); + message.projectName !== undefined && (obj.projectName = message.projectName); + message.description !== undefined && (obj.description = message.description); + message.expiresIn !== undefined && + (obj.expiresIn = message.expiresIn ? Duration.toJSON(message.expiresIn) : undefined); + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenCreateRequest { + return ProjectServiceAPITokenCreateRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): ProjectServiceAPITokenCreateRequest { + const message = createBaseProjectServiceAPITokenCreateRequest(); + message.name = object.name ?? ""; + message.projectName = object.projectName ?? ""; + message.description = object.description ?? undefined; + message.expiresIn = (object.expiresIn !== undefined && object.expiresIn !== null) + ? Duration.fromPartial(object.expiresIn) + : undefined; + return message; + }, +}; + +function createBaseProjectServiceAPITokenCreateResponse(): ProjectServiceAPITokenCreateResponse { + return { result: undefined }; +} + +export const ProjectServiceAPITokenCreateResponse = { + encode(message: ProjectServiceAPITokenCreateResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.result !== undefined) { + ProjectServiceAPITokenCreateResponse_APITokenFull.encode(message.result, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenCreateResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenCreateResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.result = ProjectServiceAPITokenCreateResponse_APITokenFull.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectServiceAPITokenCreateResponse { + return { + result: isSet(object.result) + ? ProjectServiceAPITokenCreateResponse_APITokenFull.fromJSON(object.result) + : undefined, + }; + }, + + toJSON(message: ProjectServiceAPITokenCreateResponse): unknown { + const obj: any = {}; + message.result !== undefined && (obj.result = message.result + ? ProjectServiceAPITokenCreateResponse_APITokenFull.toJSON(message.result) + : undefined); + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenCreateResponse { + return ProjectServiceAPITokenCreateResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): ProjectServiceAPITokenCreateResponse { + const message = createBaseProjectServiceAPITokenCreateResponse(); + message.result = (object.result !== undefined && object.result !== null) + ? ProjectServiceAPITokenCreateResponse_APITokenFull.fromPartial(object.result) + : undefined; + return message; + }, +}; + +function createBaseProjectServiceAPITokenCreateResponse_APITokenFull(): ProjectServiceAPITokenCreateResponse_APITokenFull { + return { item: undefined, jwt: "" }; +} + +export const ProjectServiceAPITokenCreateResponse_APITokenFull = { + encode( + message: ProjectServiceAPITokenCreateResponse_APITokenFull, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.item !== undefined) { + APITokenItem.encode(message.item, writer.uint32(10).fork()).ldelim(); + } + if (message.jwt !== "") { + writer.uint32(18).string(message.jwt); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenCreateResponse_APITokenFull { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenCreateResponse_APITokenFull(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.item = APITokenItem.decode(reader, reader.uint32()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.jwt = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectServiceAPITokenCreateResponse_APITokenFull { + return { + item: isSet(object.item) ? APITokenItem.fromJSON(object.item) : undefined, + jwt: isSet(object.jwt) ? String(object.jwt) : "", + }; + }, + + toJSON(message: ProjectServiceAPITokenCreateResponse_APITokenFull): unknown { + const obj: any = {}; + message.item !== undefined && (obj.item = message.item ? APITokenItem.toJSON(message.item) : undefined); + message.jwt !== undefined && (obj.jwt = message.jwt); + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenCreateResponse_APITokenFull { + return ProjectServiceAPITokenCreateResponse_APITokenFull.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): ProjectServiceAPITokenCreateResponse_APITokenFull { + const message = createBaseProjectServiceAPITokenCreateResponse_APITokenFull(); + message.item = (object.item !== undefined && object.item !== null) + ? APITokenItem.fromPartial(object.item) + : undefined; + message.jwt = object.jwt ?? ""; + return message; + }, +}; + +function createBaseProjectServiceAPITokenRevokeRequest(): ProjectServiceAPITokenRevokeRequest { + return { name: "" }; +} + +export const ProjectServiceAPITokenRevokeRequest = { + encode(message: ProjectServiceAPITokenRevokeRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenRevokeRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenRevokeRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectServiceAPITokenRevokeRequest { + return { name: isSet(object.name) ? String(object.name) : "" }; + }, + + toJSON(message: ProjectServiceAPITokenRevokeRequest): unknown { + const obj: any = {}; + message.name !== undefined && (obj.name = message.name); + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenRevokeRequest { + return ProjectServiceAPITokenRevokeRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): ProjectServiceAPITokenRevokeRequest { + const message = createBaseProjectServiceAPITokenRevokeRequest(); + message.name = object.name ?? ""; + return message; + }, +}; + +function createBaseProjectServiceAPITokenRevokeResponse(): ProjectServiceAPITokenRevokeResponse { + return {}; +} + +export const ProjectServiceAPITokenRevokeResponse = { + encode(_: ProjectServiceAPITokenRevokeResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenRevokeResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenRevokeResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): ProjectServiceAPITokenRevokeResponse { + return {}; + }, + + toJSON(_: ProjectServiceAPITokenRevokeResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenRevokeResponse { + return ProjectServiceAPITokenRevokeResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + _: I, + ): ProjectServiceAPITokenRevokeResponse { + const message = createBaseProjectServiceAPITokenRevokeResponse(); + return message; + }, +}; + +function createBaseProjectServiceAPITokenListRequest(): ProjectServiceAPITokenListRequest { + return { includeRevoked: false }; +} + +export const ProjectServiceAPITokenListRequest = { + encode(message: ProjectServiceAPITokenListRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.includeRevoked === true) { + writer.uint32(8).bool(message.includeRevoked); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenListRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenListRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.includeRevoked = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectServiceAPITokenListRequest { + return { includeRevoked: isSet(object.includeRevoked) ? Boolean(object.includeRevoked) : false }; + }, + + toJSON(message: ProjectServiceAPITokenListRequest): unknown { + const obj: any = {}; + message.includeRevoked !== undefined && (obj.includeRevoked = message.includeRevoked); + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenListRequest { + return ProjectServiceAPITokenListRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): ProjectServiceAPITokenListRequest { + const message = createBaseProjectServiceAPITokenListRequest(); + message.includeRevoked = object.includeRevoked ?? false; + return message; + }, +}; + +function createBaseProjectServiceAPITokenListResponse(): ProjectServiceAPITokenListResponse { + return { result: [] }; +} + +export const ProjectServiceAPITokenListResponse = { + encode(message: ProjectServiceAPITokenListResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.result) { + APITokenItem.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectServiceAPITokenListResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectServiceAPITokenListResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.result.push(APITokenItem.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectServiceAPITokenListResponse { + return { result: Array.isArray(object?.result) ? object.result.map((e: any) => APITokenItem.fromJSON(e)) : [] }; + }, + + toJSON(message: ProjectServiceAPITokenListResponse): unknown { + const obj: any = {}; + if (message.result) { + obj.result = message.result.map((e) => e ? APITokenItem.toJSON(e) : undefined); + } else { + obj.result = []; + } + return obj; + }, + + create, I>>( + base?: I, + ): ProjectServiceAPITokenListResponse { + return ProjectServiceAPITokenListResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): ProjectServiceAPITokenListResponse { + const message = createBaseProjectServiceAPITokenListResponse(); + message.result = object.result?.map((e) => APITokenItem.fromPartial(e)) || []; + return message; + }, +}; + +export interface ProjectService { + /** Project level API tokens */ + APITokenCreate( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; + APITokenList( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; + APITokenRevoke( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; +} + +export class ProjectServiceClientImpl implements ProjectService { + private readonly rpc: Rpc; + + constructor(rpc: Rpc) { + this.rpc = rpc; + this.APITokenCreate = this.APITokenCreate.bind(this); + this.APITokenList = this.APITokenList.bind(this); + this.APITokenRevoke = this.APITokenRevoke.bind(this); + } + + APITokenCreate( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary( + ProjectServiceAPITokenCreateDesc, + ProjectServiceAPITokenCreateRequest.fromPartial(request), + metadata, + ); + } + + APITokenList( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary( + ProjectServiceAPITokenListDesc, + ProjectServiceAPITokenListRequest.fromPartial(request), + metadata, + ); + } + + APITokenRevoke( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary( + ProjectServiceAPITokenRevokeDesc, + ProjectServiceAPITokenRevokeRequest.fromPartial(request), + metadata, + ); + } +} + +export const ProjectServiceDesc = { serviceName: "controlplane.v1.ProjectService" }; + +export const ProjectServiceAPITokenCreateDesc: UnaryMethodDefinitionish = { + methodName: "APITokenCreate", + service: ProjectServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return ProjectServiceAPITokenCreateRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = ProjectServiceAPITokenCreateResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const ProjectServiceAPITokenListDesc: UnaryMethodDefinitionish = { + methodName: "APITokenList", + service: ProjectServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return ProjectServiceAPITokenListRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = ProjectServiceAPITokenListResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const ProjectServiceAPITokenRevokeDesc: UnaryMethodDefinitionish = { + methodName: "APITokenRevoke", + service: ProjectServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return ProjectServiceAPITokenRevokeRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = ProjectServiceAPITokenRevokeResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +interface UnaryMethodDefinitionishR extends grpc.UnaryMethodDefinition { + requestStream: any; + responseStream: any; +} + +type UnaryMethodDefinitionish = UnaryMethodDefinitionishR; + +interface Rpc { + unary( + methodDesc: T, + request: any, + metadata: grpc.Metadata | undefined, + ): Promise; +} + +export class GrpcWebImpl { + private host: string; + private options: { + transport?: grpc.TransportFactory; + + debug?: boolean; + metadata?: grpc.Metadata; + upStreamRetryCodes?: number[]; + }; + + constructor( + host: string, + options: { + transport?: grpc.TransportFactory; + + debug?: boolean; + metadata?: grpc.Metadata; + upStreamRetryCodes?: number[]; + }, + ) { + this.host = host; + this.options = options; + } + + unary( + methodDesc: T, + _request: any, + metadata: grpc.Metadata | undefined, + ): Promise { + const request = { ..._request, ...methodDesc.requestType }; + const maybeCombinedMetadata = metadata && this.options.metadata + ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap }) + : metadata || this.options.metadata; + return new Promise((resolve, reject) => { + grpc.unary(methodDesc, { + request, + host: this.host, + metadata: maybeCombinedMetadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (response.status === grpc.Code.OK) { + resolve(response.message!.toObject()); + } else { + const err = new GrpcWebError(response.statusMessage, response.status, response.trailers); + reject(err); + } + }, + }); + }); + } +} + +declare var self: any | undefined; +declare var window: any | undefined; +declare var global: any | undefined; +var tsProtoGlobalThis: any = (() => { + if (typeof globalThis !== "undefined") { + return globalThis; + } + if (typeof self !== "undefined") { + return self; + } + if (typeof window !== "undefined") { + return window; + } + if (typeof global !== "undefined") { + return global; + } + throw "Unable to locate global object"; +})(); + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export class GrpcWebError extends tsProtoGlobalThis.Error { + constructor(message: string, public code: grpc.Code, public metadata: grpc.Metadata) { + super(message); + } +} diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts b/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts index 075c5c27f..0a210d10c 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts @@ -649,6 +649,19 @@ export interface EntityRef { entityName?: string | undefined; } +export interface APITokenItem { + id: string; + name: string; + description: string; + organizationId: string; + organizationName: string; + projectId: string; + projectName: string; + createdAt?: Date; + revokedAt?: Date; + expiresAt?: Date; +} + function createBaseWorkflowItem(): WorkflowItem { return { id: "", @@ -3922,6 +3935,192 @@ export const EntityRef = { }, }; +function createBaseAPITokenItem(): APITokenItem { + return { + id: "", + name: "", + description: "", + organizationId: "", + organizationName: "", + projectId: "", + projectName: "", + createdAt: undefined, + revokedAt: undefined, + expiresAt: undefined, + }; +} + +export const APITokenItem = { + encode(message: APITokenItem, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.name !== "") { + writer.uint32(58).string(message.name); + } + if (message.description !== "") { + writer.uint32(18).string(message.description); + } + if (message.organizationId !== "") { + writer.uint32(26).string(message.organizationId); + } + if (message.organizationName !== "") { + writer.uint32(66).string(message.organizationName); + } + if (message.projectId !== "") { + writer.uint32(74).string(message.projectId); + } + if (message.projectName !== "") { + writer.uint32(82).string(message.projectName); + } + if (message.createdAt !== undefined) { + Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(34).fork()).ldelim(); + } + if (message.revokedAt !== undefined) { + Timestamp.encode(toTimestamp(message.revokedAt), writer.uint32(42).fork()).ldelim(); + } + if (message.expiresAt !== undefined) { + Timestamp.encode(toTimestamp(message.expiresAt), writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): APITokenItem { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAPITokenItem(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.description = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.organizationId = reader.string(); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.organizationName = reader.string(); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.projectId = reader.string(); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.projectName = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.revokedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.expiresAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): APITokenItem { + return { + id: isSet(object.id) ? String(object.id) : "", + name: isSet(object.name) ? String(object.name) : "", + description: isSet(object.description) ? String(object.description) : "", + organizationId: isSet(object.organizationId) ? String(object.organizationId) : "", + organizationName: isSet(object.organizationName) ? String(object.organizationName) : "", + projectId: isSet(object.projectId) ? String(object.projectId) : "", + projectName: isSet(object.projectName) ? String(object.projectName) : "", + createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, + revokedAt: isSet(object.revokedAt) ? fromJsonTimestamp(object.revokedAt) : undefined, + expiresAt: isSet(object.expiresAt) ? fromJsonTimestamp(object.expiresAt) : undefined, + }; + }, + + toJSON(message: APITokenItem): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + message.name !== undefined && (obj.name = message.name); + message.description !== undefined && (obj.description = message.description); + message.organizationId !== undefined && (obj.organizationId = message.organizationId); + message.organizationName !== undefined && (obj.organizationName = message.organizationName); + message.projectId !== undefined && (obj.projectId = message.projectId); + message.projectName !== undefined && (obj.projectName = message.projectName); + message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); + message.revokedAt !== undefined && (obj.revokedAt = message.revokedAt.toISOString()); + message.expiresAt !== undefined && (obj.expiresAt = message.expiresAt.toISOString()); + return obj; + }, + + create, I>>(base?: I): APITokenItem { + return APITokenItem.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): APITokenItem { + const message = createBaseAPITokenItem(); + message.id = object.id ?? ""; + message.name = object.name ?? ""; + message.description = object.description ?? ""; + message.organizationId = object.organizationId ?? ""; + message.organizationName = object.organizationName ?? ""; + message.projectId = object.projectId ?? ""; + message.projectName = object.projectName ?? ""; + message.createdAt = object.createdAt ?? undefined; + message.revokedAt = object.revokedAt ?? undefined; + message.expiresAt = object.expiresAt ?? undefined; + return message; + }, +}; + declare var self: any | undefined; declare var window: any | undefined; declare var global: any | undefined; diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.jsonschema.json index 294ba695b..da9e9cc5a 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.jsonschema.json @@ -15,6 +15,12 @@ "^(organization_name)$": { "type": "string" }, + "^(project_id)$": { + "type": "string" + }, + "^(project_name)$": { + "type": "string" + }, "^(revoked_at)$": { "$ref": "google.protobuf.Timestamp.jsonschema.json" } @@ -41,6 +47,12 @@ "organizationName": { "type": "string" }, + "projectId": { + "type": "string" + }, + "projectName": { + "type": "string" + }, "revokedAt": { "$ref": "google.protobuf.Timestamp.jsonschema.json" } diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.schema.json index 38780387b..3570e4a51 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.APITokenItem.schema.json @@ -15,6 +15,12 @@ "^(organizationName)$": { "type": "string" }, + "^(projectId)$": { + "type": "string" + }, + "^(projectName)$": { + "type": "string" + }, "^(revokedAt)$": { "$ref": "google.protobuf.Timestamp.schema.json" } @@ -41,6 +47,12 @@ "organization_name": { "type": "string" }, + "project_id": { + "type": "string" + }, + "project_name": { + "type": "string" + }, "revoked_at": { "$ref": "google.protobuf.Timestamp.schema.json" } diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.jsonschema.json new file mode 100644 index 000000000..b78e9c684 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.jsonschema.json @@ -0,0 +1,32 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenCreateRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "patternProperties": { + "^(expires_in)$": { + "$ref": "google.protobuf.Duration.jsonschema.json" + }, + "^(project_name)$": { + "minLength": 1, + "type": "string" + } + }, + "properties": { + "description": { + "type": "string" + }, + "expiresIn": { + "$ref": "google.protobuf.Duration.jsonschema.json" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "projectName": { + "minLength": 1, + "type": "string" + } + }, + "title": "Project ServiceAPI Token Create Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.schema.json new file mode 100644 index 000000000..feaa0860d --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateRequest.schema.json @@ -0,0 +1,32 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenCreateRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "patternProperties": { + "^(expiresIn)$": { + "$ref": "google.protobuf.Duration.schema.json" + }, + "^(projectName)$": { + "minLength": 1, + "type": "string" + } + }, + "properties": { + "description": { + "type": "string" + }, + "expires_in": { + "$ref": "google.protobuf.Duration.schema.json" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "project_name": { + "minLength": 1, + "type": "string" + } + }, + "title": "Project ServiceAPI Token Create Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.jsonschema.json new file mode 100644 index 000000000..b5edac3cf --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.jsonschema.json @@ -0,0 +1,15 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "item": { + "$ref": "controlplane.v1.APITokenItem.jsonschema.json" + }, + "jwt": { + "type": "string" + } + }, + "title": "API Token Full", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.schema.json new file mode 100644 index 000000000..b3b8a1ae3 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.schema.json @@ -0,0 +1,15 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "item": { + "$ref": "controlplane.v1.APITokenItem.schema.json" + }, + "jwt": { + "type": "string" + } + }, + "title": "API Token Full", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.jsonschema.json new file mode 100644 index 000000000..ce0a74dde --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.jsonschema.json @@ -0,0 +1,12 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenCreateResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "result": { + "$ref": "controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.jsonschema.json" + } + }, + "title": "Project ServiceAPI Token Create Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.schema.json new file mode 100644 index 000000000..75f12266f --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenCreateResponse.schema.json @@ -0,0 +1,12 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenCreateResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "result": { + "$ref": "controlplane.v1.ProjectServiceAPITokenCreateResponse.APITokenFull.schema.json" + } + }, + "title": "Project ServiceAPI Token Create Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json new file mode 100644 index 000000000..f9aa78917 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json @@ -0,0 +1,17 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "patternProperties": { + "^(include_revoked)$": { + "type": "boolean" + } + }, + "properties": { + "includeRevoked": { + "type": "boolean" + } + }, + "title": "Project ServiceAPI Token List Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json new file mode 100644 index 000000000..a45a12a72 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json @@ -0,0 +1,17 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenListRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "patternProperties": { + "^(includeRevoked)$": { + "type": "boolean" + } + }, + "properties": { + "include_revoked": { + "type": "boolean" + } + }, + "title": "Project ServiceAPI Token List Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.jsonschema.json new file mode 100644 index 000000000..f26ba6d15 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.jsonschema.json @@ -0,0 +1,15 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenListResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "result": { + "items": { + "$ref": "controlplane.v1.APITokenItem.jsonschema.json" + }, + "type": "array" + } + }, + "title": "Project ServiceAPI Token List Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.schema.json new file mode 100644 index 000000000..9c6889421 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListResponse.schema.json @@ -0,0 +1,15 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenListResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "result": { + "items": { + "$ref": "controlplane.v1.APITokenItem.schema.json" + }, + "type": "array" + } + }, + "title": "Project ServiceAPI Token List Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json new file mode 100644 index 000000000..692519efe --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json @@ -0,0 +1,12 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + } + }, + "title": "Project ServiceAPI Token Revoke Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json new file mode 100644 index 000000000..07f01713e --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json @@ -0,0 +1,12 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + } + }, + "title": "Project ServiceAPI Token Revoke Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.jsonschema.json new file mode 100644 index 000000000..143896acd --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.jsonschema.json @@ -0,0 +1,8 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenRevokeResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": {}, + "title": "Project ServiceAPI Token Revoke Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.schema.json new file mode 100644 index 000000000..03d8f410f --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeResponse.schema.json @@ -0,0 +1,8 @@ +{ + "$id": "controlplane.v1.ProjectServiceAPITokenRevokeResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": {}, + "title": "Project ServiceAPI Token Revoke Response", + "type": "object" +} diff --git a/app/controlplane/internal/service/apitoken.go b/app/controlplane/internal/service/apitoken.go index a56aa46eb..46cc29dcb 100644 --- a/app/controlplane/internal/service/apitoken.go +++ b/app/controlplane/internal/service/apitoken.go @@ -118,5 +118,13 @@ func apiTokenBizToPb(in *biz.APIToken) *pb.APITokenItem { res.RevokedAt = timestamppb.New(*in.RevokedAt) } + if in.ProjectID != nil { + res.ProjectId = in.ProjectID.String() + } + + if in.ProjectName != nil { + res.ProjectName = *in.ProjectName + } + return res } diff --git a/app/controlplane/pkg/biz/apitoken.go b/app/controlplane/pkg/biz/apitoken.go index 04888433f..ccbb2ef09 100644 --- a/app/controlplane/pkg/biz/apitoken.go +++ b/app/controlplane/pkg/biz/apitoken.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -49,6 +49,9 @@ type APIToken struct { ExpiresAt *time.Time // When the token was manually revoked RevokedAt *time.Time + // If the token is scoped to a project + ProjectID *uuid.UUID + ProjectName *string } type APITokenRepo interface { diff --git a/app/controlplane/pkg/data/apitoken.go b/app/controlplane/pkg/data/apitoken.go index 640090c3e..b904a6e1e 100644 --- a/app/controlplane/pkg/data/apitoken.go +++ b/app/controlplane/pkg/data/apitoken.go @@ -1,5 +1,5 @@ // -// Copyright 2023 The Chainloop Authors. +// Copyright 2023-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -56,11 +56,11 @@ func (r *APITokenRepo) Create(ctx context.Context, name string, description *str return nil, fmt.Errorf("saving APIToken: %w", err) } - return entAPITokenToBiz(token), nil + return r.FindByID(ctx, token.ID) } func (r *APITokenRepo) FindByID(ctx context.Context, id uuid.UUID) (*biz.APIToken, error) { - token, err := r.data.DB.APIToken.Query().Where(apitoken.ID(id)).WithOrganization().Only(ctx) + token, err := r.data.DB.APIToken.Query().Where(apitoken.ID(id)).WithOrganization().WithProject().Only(ctx) if err != nil && !ent.IsNotFound(err) { return nil, fmt.Errorf("getting APIToken: %w", err) } else if token == nil { @@ -84,7 +84,7 @@ func (r *APITokenRepo) FindByNameInOrg(ctx context.Context, orgID uuid.UUID, nam } func (r *APITokenRepo) List(ctx context.Context, orgID *uuid.UUID, includeRevoked bool) ([]*biz.APIToken, error) { - query := r.data.DB.APIToken.Query() + query := r.data.DB.APIToken.Query().WithProject().WithOrganization() if orgID != nil { query = query.Where(apitoken.OrganizationIDEQ(*orgID)) @@ -148,5 +148,10 @@ func entAPITokenToBiz(t *ent.APIToken) *biz.APIToken { result.OrganizationName = t.Edges.Organization.Name } + if p := t.Edges.Project; p != nil { + result.ProjectID = biz.ToPtr(p.ID) + result.ProjectName = biz.ToPtr(p.Name) + } + return result } From ab5b3f72bbf8e056c0ccc06e1c1fd8058c550461 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 14:09:03 +0200 Subject: [PATCH 03/13] update token Signed-off-by: Miguel Martinez --- .../api/controlplane/v1/project.pb.go | 182 ++++++++++-------- .../api/controlplane/v1/project.proto | 14 +- .../gen/frontend/controlplane/v1/project.ts | 45 ++++- ...ServiceAPITokenListRequest.jsonschema.json | 8 + ...jectServiceAPITokenListRequest.schema.json | 8 + ...rviceAPITokenRevokeRequest.jsonschema.json | 12 ++ ...ctServiceAPITokenRevokeRequest.schema.json | 12 ++ app/controlplane/cmd/wire_gen.go | 2 + app/controlplane/internal/server/grpc.go | 2 + app/controlplane/internal/service/project.go | 100 ++++++++++ app/controlplane/internal/service/service.go | 3 +- 11 files changed, 287 insertions(+), 101 deletions(-) create mode 100644 app/controlplane/internal/service/project.go diff --git a/app/controlplane/api/controlplane/v1/project.pb.go b/app/controlplane/api/controlplane/v1/project.pb.go index 020f7ecf1..a3e5b9e93 100644 --- a/app/controlplane/api/controlplane/v1/project.pb.go +++ b/app/controlplane/api/controlplane/v1/project.pb.go @@ -26,7 +26,6 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" - _ "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -161,7 +160,9 @@ type ProjectServiceAPITokenRevokeRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // token name + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + ProjectName string `protobuf:"bytes,2,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` } func (x *ProjectServiceAPITokenRevokeRequest) Reset() { @@ -203,6 +204,13 @@ func (x *ProjectServiceAPITokenRevokeRequest) GetName() string { return "" } +func (x *ProjectServiceAPITokenRevokeRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + type ProjectServiceAPITokenRevokeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -246,7 +254,8 @@ type ProjectServiceAPITokenListRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - IncludeRevoked bool `protobuf:"varint,1,opt,name=include_revoked,json=includeRevoked,proto3" json:"include_revoked,omitempty"` + ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + IncludeRevoked bool `protobuf:"varint,2,opt,name=include_revoked,json=includeRevoked,proto3" json:"include_revoked,omitempty"` } func (x *ProjectServiceAPITokenListRequest) Reset() { @@ -281,6 +290,13 @@ func (*ProjectServiceAPITokenListRequest) Descriptor() ([]byte, []int) { return file_controlplane_v1_project_proto_rawDescGZIP(), []int{4} } +func (x *ProjectServiceAPITokenListRequest) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + func (x *ProjectServiceAPITokenListRequest) GetIncludeRevoked() bool { if x != nil { return x.IncludeRevoked @@ -402,93 +418,89 @@ var file_controlplane_v1_project_proto_rawDesc = []byte{ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x01, 0x0a, 0x23, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, - 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, - 0x3d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, - 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, - 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, - 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x22, 0xd7, 0x01, - 0x0a, 0x24, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x50, - 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, - 0x6c, 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x23, 0x50, 0x72, 0x6f, 0x6a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x01, 0x0a, 0x23, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, + 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, + 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, + 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, + 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x22, 0xd7, 0x01, 0x0a, + 0x24, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x50, 0x49, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, + 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, 0x6e, 0x0a, 0x23, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, + 0x0a, 0x21, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x6f, 0x6b, + 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x22, 0x5b, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, - 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, - 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, - 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, - 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, - 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, - 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x72, 0x6f, + 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, + 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x87, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7d, 0x0a, 0x0e, 0x41, 0x50, 0x49, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x4c, 0x0a, 0x21, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x5f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x22, - 0x5b, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x87, 0x03, 0x0a, - 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x7d, 0x0a, 0x0e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, - 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, - 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x7d, 0x0a, 0x0e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, + 0x6b, 0x65, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0e, 0x41, 0x50, 0x49, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, - 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, - 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, - 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/controlplane/api/controlplane/v1/project.proto b/app/controlplane/api/controlplane/v1/project.proto index 5ef2b3e34..9d90d0f3e 100644 --- a/app/controlplane/api/controlplane/v1/project.proto +++ b/app/controlplane/api/controlplane/v1/project.proto @@ -47,20 +47,16 @@ message ProjectServiceAPITokenCreateResponse { } message ProjectServiceAPITokenRevokeRequest { - string name = 1 [(buf.validate.field) = { - // NOTE: validations can not be shared yet https://github.com/bufbuild/protovalidate/issues/51 - cel: { - message: "must contain only lowercase letters, numbers, and hyphens." - expression: "this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')" - id: "name.dns-1123" - } - }]; + // token name + string name = 1 [(buf.validate.field).string.min_len = 1]; + string project_name = 2 [(buf.validate.field).string.min_len = 1]; } message ProjectServiceAPITokenRevokeResponse {} message ProjectServiceAPITokenListRequest { - bool include_revoked = 1; + string project_name = 1 [(buf.validate.field).string.min_len = 1]; + bool include_revoked = 2; } message ProjectServiceAPITokenListResponse { diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts index a8aa239b4..4371e408e 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts @@ -24,13 +24,16 @@ export interface ProjectServiceAPITokenCreateResponse_APITokenFull { } export interface ProjectServiceAPITokenRevokeRequest { + /** token name */ name: string; + projectName: string; } export interface ProjectServiceAPITokenRevokeResponse { } export interface ProjectServiceAPITokenListRequest { + projectName: string; includeRevoked: boolean; } @@ -291,7 +294,7 @@ export const ProjectServiceAPITokenCreateResponse_APITokenFull = { }; function createBaseProjectServiceAPITokenRevokeRequest(): ProjectServiceAPITokenRevokeRequest { - return { name: "" }; + return { name: "", projectName: "" }; } export const ProjectServiceAPITokenRevokeRequest = { @@ -299,6 +302,9 @@ export const ProjectServiceAPITokenRevokeRequest = { if (message.name !== "") { writer.uint32(10).string(message.name); } + if (message.projectName !== "") { + writer.uint32(18).string(message.projectName); + } return writer; }, @@ -316,6 +322,13 @@ export const ProjectServiceAPITokenRevokeRequest = { message.name = reader.string(); continue; + case 2: + if (tag !== 18) { + break; + } + + message.projectName = reader.string(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -326,12 +339,16 @@ export const ProjectServiceAPITokenRevokeRequest = { }, fromJSON(object: any): ProjectServiceAPITokenRevokeRequest { - return { name: isSet(object.name) ? String(object.name) : "" }; + return { + name: isSet(object.name) ? String(object.name) : "", + projectName: isSet(object.projectName) ? String(object.projectName) : "", + }; }, toJSON(message: ProjectServiceAPITokenRevokeRequest): unknown { const obj: any = {}; message.name !== undefined && (obj.name = message.name); + message.projectName !== undefined && (obj.projectName = message.projectName); return obj; }, @@ -346,6 +363,7 @@ export const ProjectServiceAPITokenRevokeRequest = { ): ProjectServiceAPITokenRevokeRequest { const message = createBaseProjectServiceAPITokenRevokeRequest(); message.name = object.name ?? ""; + message.projectName = object.projectName ?? ""; return message; }, }; @@ -399,13 +417,16 @@ export const ProjectServiceAPITokenRevokeResponse = { }; function createBaseProjectServiceAPITokenListRequest(): ProjectServiceAPITokenListRequest { - return { includeRevoked: false }; + return { projectName: "", includeRevoked: false }; } export const ProjectServiceAPITokenListRequest = { encode(message: ProjectServiceAPITokenListRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.projectName !== "") { + writer.uint32(10).string(message.projectName); + } if (message.includeRevoked === true) { - writer.uint32(8).bool(message.includeRevoked); + writer.uint32(16).bool(message.includeRevoked); } return writer; }, @@ -418,7 +439,14 @@ export const ProjectServiceAPITokenListRequest = { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - if (tag !== 8) { + if (tag !== 10) { + break; + } + + message.projectName = reader.string(); + continue; + case 2: + if (tag !== 16) { break; } @@ -434,11 +462,15 @@ export const ProjectServiceAPITokenListRequest = { }, fromJSON(object: any): ProjectServiceAPITokenListRequest { - return { includeRevoked: isSet(object.includeRevoked) ? Boolean(object.includeRevoked) : false }; + return { + projectName: isSet(object.projectName) ? String(object.projectName) : "", + includeRevoked: isSet(object.includeRevoked) ? Boolean(object.includeRevoked) : false, + }; }, toJSON(message: ProjectServiceAPITokenListRequest): unknown { const obj: any = {}; + message.projectName !== undefined && (obj.projectName = message.projectName); message.includeRevoked !== undefined && (obj.includeRevoked = message.includeRevoked); return obj; }, @@ -453,6 +485,7 @@ export const ProjectServiceAPITokenListRequest = { object: I, ): ProjectServiceAPITokenListRequest { const message = createBaseProjectServiceAPITokenListRequest(); + message.projectName = object.projectName ?? ""; message.includeRevoked = object.includeRevoked ?? false; return message; }, diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json index f9aa78917..0f6261e85 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.jsonschema.json @@ -5,11 +5,19 @@ "patternProperties": { "^(include_revoked)$": { "type": "boolean" + }, + "^(project_name)$": { + "minLength": 1, + "type": "string" } }, "properties": { "includeRevoked": { "type": "boolean" + }, + "projectName": { + "minLength": 1, + "type": "string" } }, "title": "Project ServiceAPI Token List Request", diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json index a45a12a72..d7c094871 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenListRequest.schema.json @@ -5,11 +5,19 @@ "patternProperties": { "^(includeRevoked)$": { "type": "boolean" + }, + "^(projectName)$": { + "minLength": 1, + "type": "string" } }, "properties": { "include_revoked": { "type": "boolean" + }, + "project_name": { + "minLength": 1, + "type": "string" } }, "title": "Project ServiceAPI Token List Request", diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json index 692519efe..bc6e26064 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json @@ -2,8 +2,20 @@ "$id": "controlplane.v1.ProjectServiceAPITokenRevokeRequest.jsonschema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, + "patternProperties": { + "^(project_name)$": { + "minLength": 1, + "type": "string" + } + }, "properties": { "name": { + "description": "token name", + "minLength": 1, + "type": "string" + }, + "projectName": { + "minLength": 1, "type": "string" } }, diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json index 07f01713e..4be4ad48c 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json @@ -2,8 +2,20 @@ "$id": "controlplane.v1.ProjectServiceAPITokenRevokeRequest.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, + "patternProperties": { + "^(projectName)$": { + "minLength": 1, + "type": "string" + } + }, "properties": { "name": { + "description": "token name", + "minLength": 1, + "type": "string" + }, + "project_name": { + "minLength": 1, "type": "string" } }, diff --git a/app/controlplane/cmd/wire_gen.go b/app/controlplane/cmd/wire_gen.go index 31617bf15..08c95c3c8 100644 --- a/app/controlplane/cmd/wire_gen.go +++ b/app/controlplane/cmd/wire_gen.go @@ -230,6 +230,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l groupRepo := data.NewGroupRepo(dataData, logger) groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, auditorUseCase) groupService := service.NewGroupService(groupUseCase, v5...) + projectService := service.NewProjectService(apiTokenUseCase, v5...) federatedAuthentication := bootstrap.FederatedAuthentication validator, err := newProtoValidator() if err != nil { @@ -268,6 +269,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l SigningSvc: signingService, PrometheusSvc: prometheusService, GroupSvc: groupService, + ProjectSvc: projectService, Logger: logger, ServerConfig: confServer, AuthConfig: auth, diff --git a/app/controlplane/internal/server/grpc.go b/app/controlplane/internal/server/grpc.go index 08650de65..8d6dc1090 100644 --- a/app/controlplane/internal/server/grpc.go +++ b/app/controlplane/internal/server/grpc.go @@ -83,6 +83,7 @@ type Opts struct { SigningSvc *service.SigningService PrometheusSvc *service.PrometheusService GroupSvc *service.GroupService + ProjectSvc *service.ProjectService // Utils Logger log.Logger ServerConfig *conf.Server @@ -153,6 +154,7 @@ func NewGRPCServer(opts *Opts) (*grpc.Server, error) { v1.RegisterUserServiceServer(srv, opts.UserSvc) v1.RegisterSigningServiceServer(srv, opts.SigningSvc) v1.RegisterGroupServiceServer(srv, opts.GroupSvc) + v1.RegisterProjectServiceServer(srv, opts.ProjectSvc) // Register Prometheus metrics grpc_prometheus.Register(srv.Server) diff --git a/app/controlplane/internal/service/project.go b/app/controlplane/internal/service/project.go new file mode 100644 index 000000000..e04295001 --- /dev/null +++ b/app/controlplane/internal/service/project.go @@ -0,0 +1,100 @@ +// +// Copyright 2025 The Chainloop 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. + +package service + +import ( + "context" + "time" + + pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" +) + +type ProjectService struct { + pb.UnimplementedProjectServiceServer + *service + + APITokenUseCase *biz.APITokenUseCase +} + +func NewProjectService(uc *biz.APITokenUseCase, opts ...NewOpt) *ProjectService { + return &ProjectService{ + service: newService(opts...), + APITokenUseCase: uc, + } +} + +func (s *ProjectService) APITokenCreate(ctx context.Context, req *pb.ProjectServiceAPITokenCreateRequest) (*pb.ProjectServiceAPITokenCreateResponse, error) { + currentOrg, err := requireCurrentOrg(ctx) + if err != nil { + return nil, err + } + + var expiresIn *time.Duration + if req.ExpiresIn != nil { + expiresIn = new(time.Duration) + *expiresIn = req.ExpiresIn.AsDuration() + } + + token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID) + if err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + return &pb.ProjectServiceAPITokenCreateResponse{ + Result: &pb.ProjectServiceAPITokenCreateResponse_APITokenFull{ + Item: apiTokenBizToPb(token), + Jwt: token.JWT, + }, + }, nil +} + +func (s *ProjectService) APITokenList(ctx context.Context, req *pb.ProjectServiceAPITokenListRequest) (*pb.ProjectServiceAPITokenListResponse, error) { + currentOrg, err := requireCurrentOrg(ctx) + if err != nil { + return nil, err + } + + tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked) + if err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + result := make([]*pb.APITokenItem, 0, len(tokens)) + for _, p := range tokens { + result = append(result, apiTokenBizToPb(p)) + } + + return &pb.ProjectServiceAPITokenListResponse{Result: result}, nil +} + +func (s *ProjectService) APITokenRevoke(ctx context.Context, req *pb.ProjectServiceAPITokenRevokeRequest) (*pb.ProjectServiceAPITokenRevokeResponse, error) { + currentOrg, err := requireCurrentOrg(ctx) + if err != nil { + return nil, err + } + + t, err := s.APITokenUseCase.FindByNameInOrg(ctx, currentOrg.ID, req.Name) + if err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + if err := s.APITokenUseCase.Revoke(ctx, currentOrg.ID, t.ID.String()); err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + return &pb.ProjectServiceAPITokenRevokeResponse{}, nil +} diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index a9f5e7b33..43c92e393 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -1,5 +1,5 @@ // -// Copyright 2023 The Chainloop Authors. +// Copyright 2023-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -56,6 +56,7 @@ var ProviderSet = wire.NewSet( NewSigningService, NewPrometheusService, NewGroupService, + NewProjectService, wire.Struct(new(NewWorkflowRunServiceOpts), "*"), wire.Struct(new(NewAttestationServiceOpts), "*"), wire.Struct(new(NewAttestationStateServiceOpt), "*"), From 69ed11059a794131da085d45d4bdb3c6274e1b3e Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 15:09:49 +0200 Subject: [PATCH 04/13] chore: list Signed-off-by: Miguel Martinez --- app/controlplane/internal/service/apitoken.go | 2 +- .../internal/service/attestation.go | 10 ++-- .../internal/service/attestationstate.go | 8 ++-- app/controlplane/internal/service/project.go | 25 ++++++++-- app/controlplane/internal/service/service.go | 27 +++++++---- app/controlplane/internal/service/workflow.go | 8 ++-- app/controlplane/pkg/authz/authz.go | 21 ++++++++- app/controlplane/pkg/biz/apitoken.go | 47 +++++++++++++++---- app/controlplane/pkg/data/apitoken.go | 19 ++++++-- 9 files changed, 127 insertions(+), 40 deletions(-) diff --git a/app/controlplane/internal/service/apitoken.go b/app/controlplane/internal/service/apitoken.go index 46cc29dcb..94b9bf5cc 100644 --- a/app/controlplane/internal/service/apitoken.go +++ b/app/controlplane/internal/service/apitoken.go @@ -50,7 +50,7 @@ func (s *APITokenService) Create(ctx context.Context, req *pb.APITokenServiceCre *expiresIn = req.ExpiresIn.AsDuration() } - token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID) + token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID, nil) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/internal/service/attestation.go b/app/controlplane/internal/service/attestation.go index 195ae349b..f3cae73df 100644 --- a/app/controlplane/internal/service/attestation.go +++ b/app/controlplane/internal/service/attestation.go @@ -155,7 +155,7 @@ func (s *AttestationService) Init(ctx context.Context, req *cpAPI.AttestationSer } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, org.ID, req.ProjectName, authz.PolicyWorkflowRunCreate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, org.ID, req.ProjectName, authz.PolicyWorkflowRunCreate); err != nil { return nil, err } @@ -233,7 +233,7 @@ func (s *AttestationService) Store(ctx context.Context, req *cpAPI.AttestationSe } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunCreate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunCreate); err != nil { return nil, err } @@ -369,7 +369,7 @@ func (s *AttestationService) Cancel(ctx context.Context, req *cpAPI.AttestationS } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunUpdate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunUpdate); err != nil { return nil, err } @@ -419,7 +419,7 @@ func (s *AttestationService) GetUploadCreds(ctx context.Context, req *cpAPI.Atte } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wRun.Workflow.Project, authz.PolicyWorkflowRunCreate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wRun.Workflow.Project, authz.PolicyWorkflowRunCreate); err != nil { return nil, err } @@ -670,7 +670,7 @@ func (s *AttestationService) FindOrCreateWorkflow(ctx context.Context, req *cpAP } // try to load project and apply RBAC if needed - if err := s.userHasPermissionOnProject(ctx, apiToken.OrgID, req.ProjectName, authz.PolicyWorkflowCreate); err != nil { + if _, err := s.userHasPermissionOnProject(ctx, apiToken.OrgID, req.ProjectName, authz.PolicyWorkflowCreate); err != nil { // if the project is not found, we ignore the error, since we'll create the project in this call if !errors.IsNotFound(err) { return nil, err diff --git a/app/controlplane/internal/service/attestationstate.go b/app/controlplane/internal/service/attestationstate.go index 7de437d48..1167c007e 100644 --- a/app/controlplane/internal/service/attestationstate.go +++ b/app/controlplane/internal/service/attestationstate.go @@ -69,7 +69,7 @@ func (s *AttestationStateService) Initialized(ctx context.Context, req *cpAPI.At } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunCreate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunCreate); err != nil { return nil, err } @@ -96,7 +96,7 @@ func (s *AttestationStateService) Save(ctx context.Context, req *cpAPI.Attestati } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunCreate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunCreate); err != nil { return nil, err } @@ -129,7 +129,7 @@ func (s *AttestationStateService) Read(ctx context.Context, req *cpAPI.Attestati } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunRead); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunRead); err != nil { return nil, err } @@ -163,7 +163,7 @@ func (s *AttestationStateService) Reset(ctx context.Context, req *cpAPI.Attestat } // Apply RBAC on the project - if err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunUpdate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, robotAccount.OrgID, wf.Project, authz.PolicyWorkflowRunUpdate); err != nil { return nil, err } diff --git a/app/controlplane/internal/service/project.go b/app/controlplane/internal/service/project.go index e04295001..d365e1946 100644 --- a/app/controlplane/internal/service/project.go +++ b/app/controlplane/internal/service/project.go @@ -20,6 +20,7 @@ import ( "time" pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" ) @@ -43,13 +44,19 @@ func (s *ProjectService) APITokenCreate(ctx context.Context, req *pb.ProjectServ return nil, err } + // Make sure the provided project exists and the user has permission to create tokens in it + project, err := s.userHasPermissionOnProject(ctx, currentOrg.ID, req.ProjectName, authz.PolicyProjectAPITokenCreate) + if err != nil { + return nil, err + } + var expiresIn *time.Duration if req.ExpiresIn != nil { expiresIn = new(time.Duration) *expiresIn = req.ExpiresIn.AsDuration() } - token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID) + token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID, biz.APITokenWithProjectID(project.ID)) if err != nil { return nil, handleUseCaseErr(err, s.log) } @@ -68,7 +75,13 @@ func (s *ProjectService) APITokenList(ctx context.Context, req *pb.ProjectServic return nil, err } - tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked) + // Make sure the provided project exists and the user has permission to create tokens in it + project, err := s.userHasPermissionOnProject(ctx, currentOrg.ID, req.ProjectName, authz.PolicyProjectAPITokenList) + if err != nil { + return nil, err + } + + tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked, biz.APITokenWithProjectID(project.ID)) if err != nil { return nil, handleUseCaseErr(err, s.log) } @@ -87,7 +100,13 @@ func (s *ProjectService) APITokenRevoke(ctx context.Context, req *pb.ProjectServ return nil, err } - t, err := s.APITokenUseCase.FindByNameInOrg(ctx, currentOrg.ID, req.Name) + // Make sure the provided project exists and the user has permission to create tokens in it + project, err := s.userHasPermissionOnProject(ctx, currentOrg.ID, req.ProjectName, authz.PolicyProjectAPITokenRevoke) + if err != nil { + return nil, err + } + + t, err := s.APITokenUseCase.FindByNameInOrg(ctx, currentOrg.ID, req.Name, biz.APITokenWithProjectID(project.ID)) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index 43c92e393..03f7126c0 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -182,19 +182,29 @@ func (s *service) authorizeResource(ctx context.Context, op *authz.Policy, resou } // userHasPermissionOnProject is a helper method that checks if a policy can be applied to a project. It looks for a project -// by name and ensures that the user has a role that allows that specific operation in the project. +// by name in the given organization and ensures that the user has a role that allows that specific operation in the project. // check authorizeResource method -func (s *service) userHasPermissionOnProject(ctx context.Context, orgID string, pName string, policy *authz.Policy) error { +// if it doesn't return an error, it means that the user has the permission and the project is returned +func (s *service) userHasPermissionOnProject(ctx context.Context, orgID string, pName string, policy *authz.Policy) (*biz.Project, error) { + p, err := s.projectUseCase.FindProjectByReference(ctx, orgID, &biz.EntityRef{Name: pName}) + if err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + if p == nil { + return nil, errors.NotFound("not found", "project not found") + } + + // if RBAC is not enabled, we return the project if !rbacEnabled(ctx) { - return nil + return p, nil } - p, err := s.projectUseCase.FindProjectByReference(ctx, orgID, &biz.EntityRef{Name: pName}) - if err != nil { - return handleUseCaseErr(err, s.log) + if err = s.authorizeResource(ctx, policy, authz.ResourceTypeProject, p.ID); err != nil { + return nil, err } - return s.authorizeResource(ctx, policy, authz.ResourceTypeProject, p.ID) + return p, nil } // visibleProjects returns projects where the user has any role (currently ProjectAdmin and ProjectViewer) @@ -218,7 +228,8 @@ func (s *service) visibleProjects(ctx context.Context) []uuid.UUID { // RBAC feature is enabled if the user has the `Org Member` role. func rbacEnabled(ctx context.Context) bool { - return usercontext.CurrentAuthzSubject(ctx) == string(authz.RoleOrgMember) + currentSubject := usercontext.CurrentAuthzSubject(ctx) + return currentSubject == string(authz.RoleOrgMember) } // NOTE: some of these http errors get automatically translated to gRPC status codes diff --git a/app/controlplane/internal/service/workflow.go b/app/controlplane/internal/service/workflow.go index b5d2f0aa5..34fedbebd 100644 --- a/app/controlplane/internal/service/workflow.go +++ b/app/controlplane/internal/service/workflow.go @@ -59,7 +59,7 @@ func (s *WorkflowService) Create(ctx context.Context, req *pb.WorkflowServiceCre return nil, err } - if err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.GetProjectName(), authz.PolicyWorkflowCreate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.GetProjectName(), authz.PolicyWorkflowCreate); err != nil { // if the project is not found, we ignore the error, since we'll create the project in this call if !errors.IsNotFound(err) { return nil, err @@ -100,7 +100,7 @@ func (s *WorkflowService) Update(ctx context.Context, req *pb.WorkflowServiceUpd return nil, err } - if err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.GetProjectName(), authz.PolicyWorkflowUpdate); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.GetProjectName(), authz.PolicyWorkflowUpdate); err != nil { return nil, err } @@ -243,7 +243,7 @@ func (s *WorkflowService) Delete(ctx context.Context, req *pb.WorkflowServiceDel return nil, err } - if err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.GetProjectName(), authz.PolicyWorkflowDelete); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.GetProjectName(), authz.PolicyWorkflowDelete); err != nil { return nil, err } @@ -266,7 +266,7 @@ func (s *WorkflowService) View(ctx context.Context, req *pb.WorkflowServiceViewR } // try to load project and apply RBAC if needed - if err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.ProjectName, authz.PolicyWorkflowRead); err != nil { + if _, err = s.userHasPermissionOnProject(ctx, currentOrg.ID, req.ProjectName, authz.PolicyWorkflowRead); err != nil { return nil, err } diff --git a/app/controlplane/pkg/authz/authz.go b/app/controlplane/pkg/authz/authz.go index 413f31841..997972490 100644 --- a/app/controlplane/pkg/authz/authz.go +++ b/app/controlplane/pkg/authz/authz.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -60,6 +60,7 @@ const ( ResourceProject = "project" ResourceGroup = "group" ResourceGroupMembership = "group_membership" + ResourceProjectAPIToken = "project_api_token" // We have for now three roles, viewer, admin and owner // The owner of an org @@ -140,6 +141,10 @@ var ( PolicyGroupRead = &Policy{ResourceGroup, ActionRead} // Group Memberships PolicyGroupListMemberships = &Policy{ResourceGroupMembership, ActionList} + // Project API Token + PolicyProjectAPITokenList = &Policy{ResourceProjectAPIToken, ActionList} + PolicyProjectAPITokenCreate = &Policy{ResourceProjectAPIToken, ActionCreate} + PolicyProjectAPITokenRevoke = &Policy{ResourceProjectAPIToken, ActionDelete} ) // List of policies for each role @@ -228,6 +233,11 @@ var rolesMap = map[Role][]*Policy{ // Group Memberships PolicyGroupListMemberships, + + // Project API Token + PolicyProjectAPITokenList, + PolicyProjectAPITokenCreate, + PolicyProjectAPITokenRevoke, }, // RoleProjectViewer: has read-only permissions on a project RoleProjectViewer: { @@ -255,6 +265,11 @@ var rolesMap = map[Role][]*Policy{ // integrations PolicyAttachedIntegrationAttach, PolicyAttachedIntegrationDetach, + + // Project API Token + PolicyProjectAPITokenList, + PolicyProjectAPITokenCreate, + PolicyProjectAPITokenRevoke, }, } @@ -326,6 +341,10 @@ var ServerOperationsMap = map[string][]*Policy{ "/controlplane.v1.GroupService/Delete": {PolicyGroupDelete}, // Group Memberships "/controlplane.v1.GroupService/ListMembers": {PolicyGroupListMemberships}, + // Project API Token + "/controlplane.v1.ProjectService/APITokenCreate": {PolicyProjectAPITokenCreate}, + "/controlplane.v1.ProjectService/APITokenList": {PolicyProjectAPITokenList}, + "/controlplane.v1.ProjectService/APITokenRevoke": {PolicyProjectAPITokenRevoke}, } type SubjectAPIToken struct { diff --git a/app/controlplane/pkg/biz/apitoken.go b/app/controlplane/pkg/biz/apitoken.go index ccbb2ef09..02e424ee6 100644 --- a/app/controlplane/pkg/biz/apitoken.go +++ b/app/controlplane/pkg/biz/apitoken.go @@ -55,13 +55,13 @@ type APIToken struct { } type APITokenRepo interface { - Create(ctx context.Context, name string, description *string, expiresAt *time.Time, organizationID uuid.UUID) (*APIToken, error) + Create(ctx context.Context, name string, description *string, expiresAt *time.Time, organizationID uuid.UUID, projectID *uuid.UUID) (*APIToken, error) // List all the tokens optionally filtering it by organization and including revoked tokens - List(ctx context.Context, orgID *uuid.UUID, includeRevoked bool) ([]*APIToken, error) + List(ctx context.Context, orgID *uuid.UUID, projectID *uuid.UUID, includeRevoked bool) ([]*APIToken, error) Revoke(ctx context.Context, orgID, ID uuid.UUID) error UpdateExpiration(ctx context.Context, ID uuid.UUID, expiresAt time.Time) error FindByID(ctx context.Context, ID uuid.UUID) (*APIToken, error) - FindByNameInOrg(ctx context.Context, orgID uuid.UUID, name string) (*APIToken, error) + FindByNameInOrg(ctx context.Context, orgID uuid.UUID, name string, projectID *uuid.UUID) (*APIToken, error) } type APITokenUseCase struct { @@ -127,8 +127,25 @@ func NewAPITokenUseCase(apiTokenRepo APITokenRepo, jwtConfig *APITokenJWTConfig, return uc, nil } +type apiTokenOptions struct { + ProjectID *uuid.UUID +} + +type APITokenUseCaseOpt func(*apiTokenOptions) + +func APITokenWithProjectID(projectID uuid.UUID) APITokenUseCaseOpt { + return func(o *apiTokenOptions) { + o.ProjectID = &projectID + } +} + // expires in is a string that can be parsed by time.ParseDuration -func (uc *APITokenUseCase) Create(ctx context.Context, name string, description *string, expiresIn *time.Duration, orgID string) (*APIToken, error) { +func (uc *APITokenUseCase) Create(ctx context.Context, name string, description *string, expiresIn *time.Duration, orgID string, opts ...APITokenUseCaseOpt) (*APIToken, error) { + options := &apiTokenOptions{} + for _, opt := range opts { + opt(options) + } + orgUUID, err := uuid.Parse(orgID) if err != nil { return nil, NewErrInvalidUUID(err) @@ -159,7 +176,7 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description // NOTE: the expiration time is stored just for reference, it's also encoded in the JWT // We store it since Chainloop will not have access to the JWT to check the expiration once created - token, err := uc.apiTokenRepo.Create(ctx, name, description, expiresAt, orgUUID) + token, err := uc.apiTokenRepo.Create(ctx, name, description, expiresAt, orgUUID, options.ProjectID) if err != nil { if IsErrAlreadyExists(err) { return nil, NewErrAlreadyExistsStr("name already taken") @@ -223,13 +240,18 @@ func (uc *APITokenUseCase) RegenerateJWT(ctx context.Context, tokenID uuid.UUID, return token, nil } -func (uc *APITokenUseCase) List(ctx context.Context, orgID string, includeRevoked bool) ([]*APIToken, error) { +func (uc *APITokenUseCase) List(ctx context.Context, orgID string, includeRevoked bool, opts ...APITokenUseCaseOpt) ([]*APIToken, error) { + options := &apiTokenOptions{} + for _, opt := range opts { + opt(options) + } + orgUUID, err := uuid.Parse(orgID) if err != nil { return nil, NewErrInvalidUUID(err) } - return uc.apiTokenRepo.List(ctx, &orgUUID, includeRevoked) + return uc.apiTokenRepo.List(ctx, &orgUUID, options.ProjectID, includeRevoked) } func (uc *APITokenUseCase) Revoke(ctx context.Context, orgID, id string) error { @@ -268,13 +290,18 @@ func (uc *APITokenUseCase) Revoke(ctx context.Context, orgID, id string) error { return nil } -func (uc *APITokenUseCase) FindByNameInOrg(ctx context.Context, orgID, name string) (*APIToken, error) { +func (uc *APITokenUseCase) FindByNameInOrg(ctx context.Context, orgID, name string, opts ...APITokenUseCaseOpt) (*APIToken, error) { + options := &apiTokenOptions{} + for _, opt := range opts { + opt(options) + } + orgUUID, err := uuid.Parse(orgID) if err != nil { return nil, NewErrInvalidUUID(err) } - t, err := uc.apiTokenRepo.FindByNameInOrg(ctx, orgUUID, name) + t, err := uc.apiTokenRepo.FindByNameInOrg(ctx, orgUUID, name, options.ProjectID) if err != nil { return nil, fmt.Errorf("finding token: %w", err) } @@ -310,7 +337,7 @@ func (suc *APITokenSyncerUseCase) SyncPolicies() error { suc.base.logger.Info("syncing policies for all the API tokens") // List all the non-revoked tokens from all the orgs - tokens, err := suc.base.apiTokenRepo.List(context.Background(), nil, false) + tokens, err := suc.base.apiTokenRepo.List(context.Background(), nil, nil, false) if err != nil { return fmt.Errorf("listing tokens: %w", err) } diff --git a/app/controlplane/pkg/data/apitoken.go b/app/controlplane/pkg/data/apitoken.go index b904a6e1e..8848b9363 100644 --- a/app/controlplane/pkg/data/apitoken.go +++ b/app/controlplane/pkg/data/apitoken.go @@ -41,12 +41,13 @@ func NewAPITokenRepo(data *Data, logger log.Logger) biz.APITokenRepo { } // Persist the APIToken to the database. -func (r *APITokenRepo) Create(ctx context.Context, name string, description *string, expiresAt *time.Time, organizationID uuid.UUID) (*biz.APIToken, error) { +func (r *APITokenRepo) Create(ctx context.Context, name string, description *string, expiresAt *time.Time, organizationID uuid.UUID, projectID *uuid.UUID) (*biz.APIToken, error) { token, err := r.data.DB.APIToken.Create(). SetName(name). SetNillableDescription(description). SetNillableExpiresAt(expiresAt). SetOrganizationID(organizationID). + SetNillableProjectID(projectID). Save(ctx) if err != nil { if ent.IsConstraintError(err) { @@ -70,8 +71,14 @@ func (r *APITokenRepo) FindByID(ctx context.Context, id uuid.UUID) (*biz.APIToke return entAPITokenToBiz(token), nil } -func (r *APITokenRepo) FindByNameInOrg(ctx context.Context, orgID uuid.UUID, name string) (*biz.APIToken, error) { - token, err := r.data.DB.APIToken.Query().Where(apitoken.NameEQ(name), apitoken.HasOrganizationWith(organization.ID(orgID)), apitoken.RevokedAtIsNil()).Only(ctx) +func (r *APITokenRepo) FindByNameInOrg(ctx context.Context, orgID uuid.UUID, name string, projectID *uuid.UUID) (*biz.APIToken, error) { + query := r.data.DB.APIToken.Query().Where(apitoken.NameEQ(name), apitoken.HasOrganizationWith(organization.ID(orgID)), apitoken.RevokedAtIsNil()) + + if projectID != nil { + query = query.Where(apitoken.ProjectIDEQ(*projectID)) + } + + token, err := query.Only(ctx) if err != nil { if ent.IsNotFound(err) { return nil, biz.NewErrNotFound("API token") @@ -83,13 +90,17 @@ func (r *APITokenRepo) FindByNameInOrg(ctx context.Context, orgID uuid.UUID, nam return entAPITokenToBiz(token), nil } -func (r *APITokenRepo) List(ctx context.Context, orgID *uuid.UUID, includeRevoked bool) ([]*biz.APIToken, error) { +func (r *APITokenRepo) List(ctx context.Context, orgID *uuid.UUID, projectID *uuid.UUID, includeRevoked bool) ([]*biz.APIToken, error) { query := r.data.DB.APIToken.Query().WithProject().WithOrganization() if orgID != nil { query = query.Where(apitoken.OrganizationIDEQ(*orgID)) } + if projectID != nil { + query = query.Where(apitoken.ProjectIDEQ(*projectID)) + } + if !includeRevoked { query = query.Where(apitoken.RevokedAtIsNil()) } From 720b151f5bbe3539c774dceeaf08ca7a2bec7fd3 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 15:19:34 +0200 Subject: [PATCH 05/13] chore: list Signed-off-by: Miguel Martinez --- app/controlplane/internal/service/apitoken.go | 3 ++- app/controlplane/pkg/biz/apitoken.go | 21 ++++++++++++------- app/controlplane/pkg/data/apitoken.go | 12 ++++++++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/controlplane/internal/service/apitoken.go b/app/controlplane/internal/service/apitoken.go index 94b9bf5cc..0ca0b7728 100644 --- a/app/controlplane/internal/service/apitoken.go +++ b/app/controlplane/internal/service/apitoken.go @@ -69,7 +69,8 @@ func (s *APITokenService) List(ctx context.Context, req *pb.APITokenServiceListR return nil, err } - tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked) + // Only expose system tokens + tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked, biz.APITokenShowOnlySystemTokens(true)) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/pkg/biz/apitoken.go b/app/controlplane/pkg/biz/apitoken.go index 02e424ee6..8b2ea6948 100644 --- a/app/controlplane/pkg/biz/apitoken.go +++ b/app/controlplane/pkg/biz/apitoken.go @@ -57,7 +57,7 @@ type APIToken struct { type APITokenRepo interface { Create(ctx context.Context, name string, description *string, expiresAt *time.Time, organizationID uuid.UUID, projectID *uuid.UUID) (*APIToken, error) // List all the tokens optionally filtering it by organization and including revoked tokens - List(ctx context.Context, orgID *uuid.UUID, projectID *uuid.UUID, includeRevoked bool) ([]*APIToken, error) + List(ctx context.Context, orgID *uuid.UUID, projectID *uuid.UUID, includeRevoked bool, showOnlySystemTokens bool) ([]*APIToken, error) Revoke(ctx context.Context, orgID, ID uuid.UUID) error UpdateExpiration(ctx context.Context, ID uuid.UUID, expiresAt time.Time) error FindByID(ctx context.Context, ID uuid.UUID) (*APIToken, error) @@ -128,14 +128,21 @@ func NewAPITokenUseCase(apiTokenRepo APITokenRepo, jwtConfig *APITokenJWTConfig, } type apiTokenOptions struct { - ProjectID *uuid.UUID + projectID *uuid.UUID + showOnlySystemTokens bool } type APITokenUseCaseOpt func(*apiTokenOptions) func APITokenWithProjectID(projectID uuid.UUID) APITokenUseCaseOpt { return func(o *apiTokenOptions) { - o.ProjectID = &projectID + o.projectID = &projectID + } +} + +func APITokenShowOnlySystemTokens(show bool) APITokenUseCaseOpt { + return func(o *apiTokenOptions) { + o.showOnlySystemTokens = show } } @@ -176,7 +183,7 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description // NOTE: the expiration time is stored just for reference, it's also encoded in the JWT // We store it since Chainloop will not have access to the JWT to check the expiration once created - token, err := uc.apiTokenRepo.Create(ctx, name, description, expiresAt, orgUUID, options.ProjectID) + token, err := uc.apiTokenRepo.Create(ctx, name, description, expiresAt, orgUUID, options.projectID) if err != nil { if IsErrAlreadyExists(err) { return nil, NewErrAlreadyExistsStr("name already taken") @@ -251,7 +258,7 @@ func (uc *APITokenUseCase) List(ctx context.Context, orgID string, includeRevoke return nil, NewErrInvalidUUID(err) } - return uc.apiTokenRepo.List(ctx, &orgUUID, options.ProjectID, includeRevoked) + return uc.apiTokenRepo.List(ctx, &orgUUID, options.projectID, includeRevoked, options.showOnlySystemTokens) } func (uc *APITokenUseCase) Revoke(ctx context.Context, orgID, id string) error { @@ -301,7 +308,7 @@ func (uc *APITokenUseCase) FindByNameInOrg(ctx context.Context, orgID, name stri return nil, NewErrInvalidUUID(err) } - t, err := uc.apiTokenRepo.FindByNameInOrg(ctx, orgUUID, name, options.ProjectID) + t, err := uc.apiTokenRepo.FindByNameInOrg(ctx, orgUUID, name, options.projectID) if err != nil { return nil, fmt.Errorf("finding token: %w", err) } @@ -337,7 +344,7 @@ func (suc *APITokenSyncerUseCase) SyncPolicies() error { suc.base.logger.Info("syncing policies for all the API tokens") // List all the non-revoked tokens from all the orgs - tokens, err := suc.base.apiTokenRepo.List(context.Background(), nil, nil, false) + tokens, err := suc.base.apiTokenRepo.List(context.Background(), nil, nil, false, false) if err != nil { return fmt.Errorf("listing tokens: %w", err) } diff --git a/app/controlplane/pkg/data/apitoken.go b/app/controlplane/pkg/data/apitoken.go index 8848b9363..87288161d 100644 --- a/app/controlplane/pkg/data/apitoken.go +++ b/app/controlplane/pkg/data/apitoken.go @@ -90,13 +90,23 @@ func (r *APITokenRepo) FindByNameInOrg(ctx context.Context, orgID uuid.UUID, nam return entAPITokenToBiz(token), nil } -func (r *APITokenRepo) List(ctx context.Context, orgID *uuid.UUID, projectID *uuid.UUID, includeRevoked bool) ([]*biz.APIToken, error) { +func (r *APITokenRepo) List(ctx context.Context, orgID *uuid.UUID, projectID *uuid.UUID, includeRevoked bool, showOnlySystemTokens bool) ([]*biz.APIToken, error) { query := r.data.DB.APIToken.Query().WithProject().WithOrganization() if orgID != nil { query = query.Where(apitoken.OrganizationIDEQ(*orgID)) } + if showOnlySystemTokens && projectID != nil { + return nil, fmt.Errorf("projectID cannot be provided when skipProjectScopedTokens is true") + } + + if showOnlySystemTokens { + query = query.Where(apitoken.ProjectIDIsNil()) + } else if projectID != nil { + query = query.Where(apitoken.ProjectIDEQ(*projectID)) + } + if projectID != nil { query = query.Where(apitoken.ProjectIDEQ(*projectID)) } From ce1a465218039da39b0d10a21befd953f52404f4 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 18:12:45 +0200 Subject: [PATCH 06/13] fix test Signed-off-by: Miguel Martinez --- .../api/controlplane/v1/api_token.pb.go | 145 +++++++++--------- .../api/controlplane/v1/api_token.proto | 3 +- .../api/controlplane/v1/api_token_grpc.pb.go | 2 +- app/controlplane/internal/service/service.go | 4 - app/controlplane/pkg/data/ent/schema-viz.html | 6 +- 5 files changed, 74 insertions(+), 86 deletions(-) diff --git a/app/controlplane/api/controlplane/v1/api_token.pb.go b/app/controlplane/api/controlplane/v1/api_token.pb.go index 835bb5907..d205bc5b9 100644 --- a/app/controlplane/api/controlplane/v1/api_token.pb.go +++ b/app/controlplane/api/controlplane/v1/api_token.pb.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" - _ "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -394,83 +393,81 @@ var file_controlplane_v1_api_token_proto_rawDesc = []byte{ 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x01, 0x0a, 0x1c, 0x41, 0x50, - 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, - 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, - 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, - 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, - 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x22, 0xc9, 0x01, 0x0a, - 0x1d, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, - 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x01, 0x0a, 0x1c, 0x41, 0x50, 0x49, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, + 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x09, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, + 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, + 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x22, 0xc9, 0x01, 0x0a, 0x1d, + 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, + 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x50, + 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, + 0x6c, 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, 0xb8, 0x01, 0x0a, 0x1c, 0x41, 0x50, 0x49, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, + 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, + 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, + 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, + 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x45, 0x0a, 0x1a, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x76, + 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x41, 0x50, + 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x32, 0xc6, 0x02, 0x0a, 0x0f, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, - 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x1a, 0x53, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, - 0x75, 0x6c, 0x6c, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, - 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x22, 0xb8, 0x01, 0x0a, 0x1c, 0x41, 0x50, 0x49, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, - 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, - 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, - 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, - 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, - 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, - 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, - 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x45, 0x0a, 0x1a, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, + 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x41, - 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x32, 0xc6, 0x02, 0x0a, 0x0f, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, - 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, - 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x67, 0x0a, 0x06, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, - 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, - 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x67, 0x0a, 0x06, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, - 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, - 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, - 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x6b, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, + 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, + 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/controlplane/api/controlplane/v1/api_token.proto b/app/controlplane/api/controlplane/v1/api_token.proto index 6bc5841cc..b7f160a4e 100644 --- a/app/controlplane/api/controlplane/v1/api_token.proto +++ b/app/controlplane/api/controlplane/v1/api_token.proto @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ package controlplane.v1; import "buf/validate/validate.proto"; import "controlplane/v1/response_messages.proto"; import "google/protobuf/duration.proto"; -import "google/protobuf/timestamp.proto"; option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1;v1"; diff --git a/app/controlplane/api/controlplane/v1/api_token_grpc.pb.go b/app/controlplane/api/controlplane/v1/api_token_grpc.pb.go index 9a780e579..a7791b48e 100644 --- a/app/controlplane/api/controlplane/v1/api_token_grpc.pb.go +++ b/app/controlplane/api/controlplane/v1/api_token_grpc.pb.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index 03f7126c0..abe0e4d20 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -191,10 +191,6 @@ func (s *service) userHasPermissionOnProject(ctx context.Context, orgID string, return nil, handleUseCaseErr(err, s.log) } - if p == nil { - return nil, errors.NotFound("not found", "project not found") - } - // if RBAC is not enabled, we return the project if !rbacEnabled(ctx) { return p, nil diff --git a/app/controlplane/pkg/data/ent/schema-viz.html b/app/controlplane/pkg/data/ent/schema-viz.html index efd9cafc6..cfb23d927 100644 --- a/app/controlplane/pkg/data/ent/schema-viz.html +++ b/app/controlplane/pkg/data/ent/schema-viz.html @@ -70,11 +70,7 @@ } -<<<<<<< HEAD - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Group\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"GroupMembership\",\"fields\":[{\"name\":\"group_id\",\"type\":\"uuid.UUID\"},{\"name\":\"user_id\",\"type\":\"uuid.UUID\"},{\"name\":\"maintainer\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"APIToken\",\"to\":\"Project\",\"label\":\"project\"},{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"Group\",\"to\":\"User\",\"label\":\"members\"},{\"from\":\"GroupMembership\",\"to\":\"Group\",\"label\":\"group\"},{\"from\":\"GroupMembership\",\"to\":\"User\",\"label\":\"user\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Organization\",\"to\":\"Group\",\"label\":\"groups\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); -======= - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Group\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"GroupMembership\",\"fields\":[{\"name\":\"group_id\",\"type\":\"uuid.UUID\"},{\"name\":\"user_id\",\"type\":\"uuid.UUID\"},{\"name\":\"maintainer\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"CASMapping\",\"to\":\"Project\",\"label\":\"project\"},{\"from\":\"Group\",\"to\":\"User\",\"label\":\"members\"},{\"from\":\"GroupMembership\",\"to\":\"Group\",\"label\":\"group\"},{\"from\":\"GroupMembership\",\"to\":\"User\",\"label\":\"user\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Organization\",\"to\":\"Group\",\"label\":\"groups\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); ->>>>>>> main + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Group\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"GroupMembership\",\"fields\":[{\"name\":\"group_id\",\"type\":\"uuid.UUID\"},{\"name\":\"user_id\",\"type\":\"uuid.UUID\"},{\"name\":\"maintainer\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"APIToken\",\"to\":\"Project\",\"label\":\"project\"},{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"CASMapping\",\"to\":\"Project\",\"label\":\"project\"},{\"from\":\"Group\",\"to\":\"User\",\"label\":\"members\"},{\"from\":\"GroupMembership\",\"to\":\"Group\",\"label\":\"group\"},{\"from\":\"GroupMembership\",\"to\":\"User\",\"label\":\"user\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Organization\",\"to\":\"Group\",\"label\":\"groups\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, From cf7cc4a3bdb9afcca6240b69134ec7a0d3e9ef7a Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 18:28:46 +0200 Subject: [PATCH 07/13] fix test Signed-off-by: Miguel Martinez --- .../pkg/biz/apitoken_integration_test.go | 77 +++++++++++++++++-- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/app/controlplane/pkg/biz/apitoken_integration_test.go b/app/controlplane/pkg/biz/apitoken_integration_test.go index 3edeb78fd..0989095c3 100644 --- a/app/controlplane/pkg/biz/apitoken_integration_test.go +++ b/app/controlplane/pkg/biz/apitoken_integration_test.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -65,11 +65,20 @@ func (s *apiTokenTestSuite) TestCreate() { s.Nil(token.RevokedAt) }) + s.Run("happy path with project", func() { + token, err := s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProjectID(s.p1.ID)) + s.NoError(err) + s.Equal(s.org.ID, token.OrganizationID.String()) + s.Equal(s.p1.ID, *token.ProjectID) + s.Equal(s.p1.Name, *token.ProjectName) + }) + s.Run("testing name uniqueness", func() { testCases := []struct { name string tokenName string wantErrMsg string + projectID *uuid.UUID }{ { name: "name missing", @@ -95,11 +104,32 @@ func (s *apiTokenTestSuite) TestCreate() { tokenName: "my-name", wantErrMsg: "name already taken", }, + { + name: "tokens in projects can have the same name", + tokenName: "my-name", + projectID: &s.p1.ID, + }, + { + name: "tokens in different projects too", + tokenName: "my-name", + projectID: &s.p2.ID, + }, + { + name: "can't be duplicated in the same project", + tokenName: "my-name", + projectID: &s.p1.ID, + wantErrMsg: "name already taken", + }, } for _, tc := range testCases { s.Run(tc.name, func() { - token, err := s.APIToken.Create(ctx, tc.tokenName, nil, nil, s.org.ID) + var opts []biz.APITokenUseCaseOpt + if tc.projectID != nil { + opts = append(opts, biz.APITokenWithProjectID(*tc.projectID)) + } + + token, err := s.APIToken.Create(ctx, tc.tokenName, nil, nil, s.org.ID, opts...) if tc.wantErrMsg != "" { s.Error(err) s.Contains(err.Error(), tc.wantErrMsg) @@ -224,11 +254,11 @@ func (s *apiTokenTestSuite) TestList() { s.Len(tokens, 0) }) - s.Run("returns the tokens for that org", func() { + s.Run("returns all tokens for that org both system and project scoped", func() { var err error tokens, err := s.APIToken.List(ctx, s.org.ID, false) s.NoError(err) - require.Len(s.T(), tokens, 2) + require.Len(s.T(), tokens, 4) s.Equal(s.t1.ID, tokens[0].ID) s.Equal(s.t2.ID, tokens[1].ID) // It has a name set @@ -241,13 +271,31 @@ func (s *apiTokenTestSuite) TestList() { s.Equal(s.t3.ID, tokens[0].ID) }) + s.Run("can return only for a specific project", func() { + var err error + tokens, err := s.APIToken.List(ctx, s.org.ID, false, biz.APITokenWithProjectID(s.p1.ID)) + s.NoError(err) + require.Len(s.T(), tokens, 2) + s.Equal(s.t4.ID, tokens[0].ID) + s.Equal(s.t5.ID, tokens[1].ID) + }) + + s.Run("or just the system tokens", func() { + var err error + tokens, err := s.APIToken.List(ctx, s.org.ID, false, biz.APITokenShowOnlySystemTokens(true)) + s.NoError(err) + require.Len(s.T(), tokens, 2) + s.Equal(s.t1.ID, tokens[0].ID) + s.Equal(s.t2.ID, tokens[1].ID) + }) + s.Run("doesn't return revoked by default", func() { // revoke one token err := s.APIToken.Revoke(ctx, s.org.ID, s.t1.ID.String()) require.NoError(s.T(), err) tokens, err := s.APIToken.List(ctx, s.org.ID, false) s.NoError(err) - require.Len(s.T(), tokens, 1) + require.Len(s.T(), tokens, 3) s.Equal(s.t2.ID, tokens[0].ID) }) @@ -255,7 +303,7 @@ func (s *apiTokenTestSuite) TestList() { // revoke one token tokens, err := s.APIToken.List(ctx, s.org.ID, true) s.NoError(err) - require.Len(s.T(), tokens, 2) + require.Len(s.T(), tokens, 4) s.Equal(s.t1.ID, tokens[0].ID) s.Equal(s.t2.ID, tokens[1].ID) }) @@ -288,8 +336,9 @@ func TestAPITokenUseCase(t *testing.T) { // Utility struct to hold the test suite type apiTokenTestSuite struct { testhelpers.UseCasesEachTestSuite - org, org2 *biz.Organization - t1, t2, t3 *biz.APIToken + org, org2 *biz.Organization + t1, t2, t3, t4, t5 *biz.APIToken + p1, p2 *biz.Project } func (s *apiTokenTestSuite) SetupTest() { @@ -304,6 +353,12 @@ func (s *apiTokenTestSuite) SetupTest() { s.org2, err = s.Organization.CreateWithRandomName(ctx) assert.NoError(err) + s.p1, err = s.Project.Create(ctx, s.org.ID, "project1") + require.NoError(s.T(), err) + + s.p2, err = s.Project.Create(ctx, s.org.ID, "project2") + require.NoError(s.T(), err) + // Create 2 tokens for org 1 s.t1, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID) require.NoError(s.T(), err) @@ -312,4 +367,10 @@ func (s *apiTokenTestSuite) SetupTest() { // and 1 token for org 2 s.t3, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org2.ID) require.NoError(s.T(), err) + + // Create 2 tokens for project 1 + s.t4, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProjectID(s.p1.ID)) + require.NoError(s.T(), err) + s.t5, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProjectID(s.p1.ID)) + require.NoError(s.T(), err) } From 4ba9e631b4cd06988f39a0431b43899f0ed6fdee Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 18:48:29 +0200 Subject: [PATCH 08/13] fix test Signed-off-by: Miguel Martinez --- .../gen/frontend/google/protobuf/descriptor.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts b/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts index 0d2d2fb32..d59b21da4 100644 --- a/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts +++ b/app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts @@ -30,7 +30,7 @@ export enum Edition { EDITION_2024 = 1001, /** * EDITION_1_TEST_ONLY - Placeholder editions for testing feature resolution. These should not be - * used or relyed on outside of tests. + * used or relied on outside of tests. */ EDITION_1_TEST_ONLY = 1, EDITION_2_TEST_ONLY = 2, @@ -875,12 +875,13 @@ export interface MessageOptions { export interface FieldOptions { /** + * NOTE: ctype is deprecated. Use `features.(pb.cpp).string_type` instead. * The ctype option instructs the C++ code generator to use a different * representation of the field than it normally would. See the specific * options below. This option is only implemented to support use of * [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of - * type "bytes" in the open source release -- sorry, we'll try to include - * other types in a future version! + * type "bytes" in the open source release. + * TODO: make ctype actually deprecated. */ ctype: FieldOptions_CType; /** @@ -1052,11 +1053,7 @@ export function fieldOptions_JSTypeToJSON(object: FieldOptions_JSType): string { } } -/** - * If set to RETENTION_SOURCE, the option will be omitted from the binary. - * Note: as of January 2023, support for this is in progress and does not yet - * have an effect (b/264593489). - */ +/** If set to RETENTION_SOURCE, the option will be omitted from the binary. */ export enum FieldOptions_OptionRetention { RETENTION_UNKNOWN = 0, RETENTION_RUNTIME = 1, @@ -1099,8 +1096,7 @@ export function fieldOptions_OptionRetentionToJSON(object: FieldOptions_OptionRe /** * This indicates the types of entities that the field may apply to when used * as an option. If it is unset, then the field may be freely used as an - * option on any kind of entity. Note: as of January 2023, support for this is - * in progress and does not yet have an effect (b/264593489). + * option on any kind of entity. */ export enum FieldOptions_OptionTargetType { TARGET_TYPE_UNKNOWN = 0, From e720034917357938a33b6fb100089921b59e5be0 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 21:40:41 +0200 Subject: [PATCH 09/13] chore: generate token Signed-off-by: Miguel Martinez --- app/controlplane/internal/service/apitoken.go | 2 +- app/controlplane/internal/service/project.go | 6 +- app/controlplane/pkg/biz/apitoken.go | 54 ++++++- .../pkg/biz/apitoken_integration_test.go | 10 +- app/controlplane/pkg/jwt/apitoken/apitoken.go | 60 ++++++-- .../pkg/jwt/apitoken/apitoken_test.go | 144 +++++++++++++++--- 6 files changed, 226 insertions(+), 50 deletions(-) diff --git a/app/controlplane/internal/service/apitoken.go b/app/controlplane/internal/service/apitoken.go index 0ca0b7728..034a70196 100644 --- a/app/controlplane/internal/service/apitoken.go +++ b/app/controlplane/internal/service/apitoken.go @@ -50,7 +50,7 @@ func (s *APITokenService) Create(ctx context.Context, req *pb.APITokenServiceCre *expiresIn = req.ExpiresIn.AsDuration() } - token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID, nil) + token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/internal/service/project.go b/app/controlplane/internal/service/project.go index d365e1946..4a3e56b4d 100644 --- a/app/controlplane/internal/service/project.go +++ b/app/controlplane/internal/service/project.go @@ -56,7 +56,7 @@ func (s *ProjectService) APITokenCreate(ctx context.Context, req *pb.ProjectServ *expiresIn = req.ExpiresIn.AsDuration() } - token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID, biz.APITokenWithProjectID(project.ID)) + token, err := s.APITokenUseCase.Create(ctx, req.Name, req.Description, expiresIn, currentOrg.ID, biz.APITokenWithProject(project)) if err != nil { return nil, handleUseCaseErr(err, s.log) } @@ -81,7 +81,7 @@ func (s *ProjectService) APITokenList(ctx context.Context, req *pb.ProjectServic return nil, err } - tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked, biz.APITokenWithProjectID(project.ID)) + tokens, err := s.APITokenUseCase.List(ctx, currentOrg.ID, req.IncludeRevoked, biz.APITokenWithProject(project)) if err != nil { return nil, handleUseCaseErr(err, s.log) } @@ -106,7 +106,7 @@ func (s *ProjectService) APITokenRevoke(ctx context.Context, req *pb.ProjectServ return nil, err } - t, err := s.APITokenUseCase.FindByNameInOrg(ctx, currentOrg.ID, req.Name, biz.APITokenWithProjectID(project.ID)) + t, err := s.APITokenUseCase.FindByNameInOrg(ctx, currentOrg.ID, req.Name, biz.APITokenWithProject(project)) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/pkg/biz/apitoken.go b/app/controlplane/pkg/biz/apitoken.go index 8b2ea6948..030d3737a 100644 --- a/app/controlplane/pkg/biz/apitoken.go +++ b/app/controlplane/pkg/biz/apitoken.go @@ -128,15 +128,15 @@ func NewAPITokenUseCase(apiTokenRepo APITokenRepo, jwtConfig *APITokenJWTConfig, } type apiTokenOptions struct { - projectID *uuid.UUID + project *Project showOnlySystemTokens bool } type APITokenUseCaseOpt func(*apiTokenOptions) -func APITokenWithProjectID(projectID uuid.UUID) APITokenUseCaseOpt { +func APITokenWithProject(project *Project) APITokenUseCaseOpt { return func(o *apiTokenOptions) { - o.projectID = &projectID + o.project = project } } @@ -181,9 +181,15 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description return nil, fmt.Errorf("finding organization: %w", err) } + // If a project is provided, we store it in the token + var projectID *uuid.UUID + if options.project != nil { + projectID = ToPtr(options.project.ID) + } + // NOTE: the expiration time is stored just for reference, it's also encoded in the JWT // We store it since Chainloop will not have access to the JWT to check the expiration once created - token, err := uc.apiTokenRepo.Create(ctx, name, description, expiresAt, orgUUID, options.projectID) + token, err := uc.apiTokenRepo.Create(ctx, name, description, expiresAt, orgUUID, projectID) if err != nil { if IsErrAlreadyExists(err) { return nil, NewErrAlreadyExistsStr("name already taken") @@ -191,8 +197,22 @@ func (uc *APITokenUseCase) Create(ctx context.Context, name string, description return nil, fmt.Errorf("storing token: %w", err) } + generationOpts := &apitoken.GenerateJWTOptions{ + OrgID: token.OrganizationID, + OrgName: org.Name, + KeyID: token.ID, + KeyName: name, + ExpiresAt: expiresAt, + } + + if projectID != nil { + generationOpts.ProjectID = ToPtr(options.project.ID) + generationOpts.ProjectName = ToPtr(options.project.Name) + } + // generate the JWT - token.JWT, err = uc.jwtBuilder.GenerateJWT(token.OrganizationID.String(), org.Name, token.ID.String(), expiresAt) + token.JWT, err = uc.jwtBuilder.GenerateJWT(generationOpts) + if err != nil { return nil, fmt.Errorf("generating jwt: %w", err) } @@ -233,8 +253,16 @@ func (uc *APITokenUseCase) RegenerateJWT(ctx context.Context, tokenID uuid.UUID, return nil, fmt.Errorf("finding organization: %w", err) } + generationOpts := &apitoken.GenerateJWTOptions{ + OrgID: token.OrganizationID, + OrgName: org.Name, + KeyID: token.ID, + KeyName: token.Name, + ExpiresAt: &expiresAt, + } + // generate the JWT - token.JWT, err = uc.jwtBuilder.GenerateJWT(token.OrganizationID.String(), org.Name, token.ID.String(), &expiresAt) + token.JWT, err = uc.jwtBuilder.GenerateJWT(generationOpts) if err != nil { return nil, fmt.Errorf("generating jwt: %w", err) } @@ -258,7 +286,12 @@ func (uc *APITokenUseCase) List(ctx context.Context, orgID string, includeRevoke return nil, NewErrInvalidUUID(err) } - return uc.apiTokenRepo.List(ctx, &orgUUID, options.projectID, includeRevoked, options.showOnlySystemTokens) + var projectID *uuid.UUID + if options.project != nil { + projectID = ToPtr(options.project.ID) + } + + return uc.apiTokenRepo.List(ctx, &orgUUID, projectID, includeRevoked, options.showOnlySystemTokens) } func (uc *APITokenUseCase) Revoke(ctx context.Context, orgID, id string) error { @@ -308,7 +341,12 @@ func (uc *APITokenUseCase) FindByNameInOrg(ctx context.Context, orgID, name stri return nil, NewErrInvalidUUID(err) } - t, err := uc.apiTokenRepo.FindByNameInOrg(ctx, orgUUID, name, options.projectID) + var projectID *uuid.UUID + if options.project != nil { + projectID = ToPtr(options.project.ID) + } + + t, err := uc.apiTokenRepo.FindByNameInOrg(ctx, orgUUID, name, projectID) if err != nil { return nil, fmt.Errorf("finding token: %w", err) } diff --git a/app/controlplane/pkg/biz/apitoken_integration_test.go b/app/controlplane/pkg/biz/apitoken_integration_test.go index 0989095c3..666a1dfce 100644 --- a/app/controlplane/pkg/biz/apitoken_integration_test.go +++ b/app/controlplane/pkg/biz/apitoken_integration_test.go @@ -66,7 +66,7 @@ func (s *apiTokenTestSuite) TestCreate() { }) s.Run("happy path with project", func() { - token, err := s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProjectID(s.p1.ID)) + token, err := s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProject(s.p1)) s.NoError(err) s.Equal(s.org.ID, token.OrganizationID.String()) s.Equal(s.p1.ID, *token.ProjectID) @@ -126,7 +126,7 @@ func (s *apiTokenTestSuite) TestCreate() { s.Run(tc.name, func() { var opts []biz.APITokenUseCaseOpt if tc.projectID != nil { - opts = append(opts, biz.APITokenWithProjectID(*tc.projectID)) + opts = append(opts, biz.APITokenWithProject(s.p1)) } token, err := s.APIToken.Create(ctx, tc.tokenName, nil, nil, s.org.ID, opts...) @@ -273,7 +273,7 @@ func (s *apiTokenTestSuite) TestList() { s.Run("can return only for a specific project", func() { var err error - tokens, err := s.APIToken.List(ctx, s.org.ID, false, biz.APITokenWithProjectID(s.p1.ID)) + tokens, err := s.APIToken.List(ctx, s.org.ID, false, biz.APITokenWithProject(s.p1)) s.NoError(err) require.Len(s.T(), tokens, 2) s.Equal(s.t4.ID, tokens[0].ID) @@ -369,8 +369,8 @@ func (s *apiTokenTestSuite) SetupTest() { require.NoError(s.T(), err) // Create 2 tokens for project 1 - s.t4, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProjectID(s.p1.ID)) + s.t4, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProject(s.p1)) require.NoError(s.T(), err) - s.t5, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProjectID(s.p1.ID)) + s.t5, err = s.APIToken.Create(ctx, randomName(), nil, nil, s.org.ID, biz.APITokenWithProject(s.p1)) require.NoError(s.T(), err) } diff --git a/app/controlplane/pkg/jwt/apitoken/apitoken.go b/app/controlplane/pkg/jwt/apitoken/apitoken.go index 5fa50fff6..a9be059bd 100644 --- a/app/controlplane/pkg/jwt/apitoken/apitoken.go +++ b/app/controlplane/pkg/jwt/apitoken/apitoken.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "time" "github.com/golang-jwt/jwt/v4" + "github.com/google/uuid" ) var SigningMethod = jwt.SigningMethodHS256 @@ -66,22 +67,58 @@ func NewBuilder(opts ...NewOpt) (*Builder, error) { return b, nil } +type GenerateJWTOptions struct { + OrgID uuid.UUID + OrgName string + KeyID uuid.UUID + KeyName string + ProjectID *uuid.UUID + ProjectName *string + ExpiresAt *time.Time +} + // GenerateJWT creates a new JWT token for the given organization and keyID -func (ra *Builder) GenerateJWT(orgID, orgName, keyID string, expiresAt *time.Time) (string, error) { +func (ra *Builder) GenerateJWT(opts *GenerateJWTOptions) (string, error) { + if opts == nil { + return "", errors.New("options are required") + } + + if opts.OrgID == uuid.Nil { + return "", errors.New("orgID is required") + } + + if opts.OrgName == "" { + return "", errors.New("orgName is required") + } + + if opts.KeyID == uuid.Nil { + return "", errors.New("keyID is required") + } + + if opts.KeyName == "" { + return "", errors.New("keyName is required") + } + claims := CustomClaims{ - orgID, - orgName, - jwt.RegisteredClaims{ + OrgID: opts.OrgID.String(), + OrgName: opts.OrgName, + KeyName: opts.KeyName, + RegisteredClaims: jwt.RegisteredClaims{ // Key identifier so we can check its revocation status - ID: keyID, + ID: opts.KeyID.String(), Issuer: ra.issuer, Audience: jwt.ClaimStrings{Audience}, }, } + if opts.ProjectID != nil { + claims.ProjectID = opts.ProjectID.String() + claims.ProjectName = *opts.ProjectName + } + // optional expiration value, i.e 30 days - if expiresAt != nil { - claims.ExpiresAt = jwt.NewNumericDate(*expiresAt) + if opts.ExpiresAt != nil { + claims.ExpiresAt = jwt.NewNumericDate(*opts.ExpiresAt) } resultToken := jwt.NewWithClaims(SigningMethod, claims) @@ -89,7 +126,10 @@ func (ra *Builder) GenerateJWT(orgID, orgName, keyID string, expiresAt *time.Tim } type CustomClaims struct { - OrgID string `json:"org_id"` - OrgName string `json:"org_name"` + OrgID string `json:"org_id"` + OrgName string `json:"org_name"` + KeyName string `json:"token_name"` + ProjectID string `json:"project_id,omitempty"` + ProjectName string `json:"project_name,omitempty"` jwt.RegisteredClaims } diff --git a/app/controlplane/pkg/jwt/apitoken/apitoken_test.go b/app/controlplane/pkg/jwt/apitoken/apitoken_test.go index 27a28bbbb..e04aaec39 100644 --- a/app/controlplane/pkg/jwt/apitoken/apitoken_test.go +++ b/app/controlplane/pkg/jwt/apitoken/apitoken_test.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "time" "github.com/golang-jwt/jwt/v4" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -69,30 +70,127 @@ func TestNewBuilder(t *testing.T) { func TestGenerateJWT(t *testing.T) { const hmacSecret = "my-secret" + testCases := []struct { + name string + opts *GenerateJWTOptions + wantErr bool + }{ + { + name: "no project", + opts: &GenerateJWTOptions{ + OrgID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + OrgName: "org-name", + KeyName: "key-name", + KeyID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + ExpiresAt: toPtr(time.Now().Add(1 * time.Hour)), + }, + }, + { + name: "no expiration", + opts: &GenerateJWTOptions{ + OrgID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + OrgName: "org-name", + KeyName: "key-name", + KeyID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + }, + }, + { + name: "with project", + opts: &GenerateJWTOptions{ + OrgID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + OrgName: "org-name", + KeyName: "key-name", + KeyID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + ProjectID: toPtr(uuid.MustParse("123e4567-e89b-12d3-a456-426614174000")), + ProjectName: toPtr("project-name"), + ExpiresAt: toPtr(time.Now().Add(1 * time.Hour)), + }, + }, + { + name: "missing orgID", + opts: &GenerateJWTOptions{ + OrgName: "org-name", + KeyName: "key-name", + KeyID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + ExpiresAt: toPtr(time.Now().Add(1 * time.Hour)), + }, + wantErr: true, + }, + { + name: "missing orgName", + opts: &GenerateJWTOptions{ + OrgID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + KeyName: "key-name", + KeyID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + ExpiresAt: toPtr(time.Now().Add(1 * time.Hour)), + }, + wantErr: true, + }, + { + name: "missing keyID", + opts: &GenerateJWTOptions{ + OrgID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + OrgName: "org-name", + KeyName: "key-name", + ExpiresAt: toPtr(time.Now().Add(1 * time.Hour)), + }, + wantErr: true, + }, + { + name: "missing keyName", + opts: &GenerateJWTOptions{ + OrgID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + OrgName: "org-name", + KeyID: uuid.MustParse("123e4567-e89b-12d3-a456-426614174000"), + ExpiresAt: toPtr(time.Now().Add(1 * time.Hour)), + }, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + b, err := NewBuilder(WithIssuer("my-issuer"), WithKeySecret(hmacSecret)) + require.NoError(t, err) + + token, err := b.GenerateJWT(tc.opts) + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + assert.NotEmpty(t, token) + + claims := &CustomClaims{} + tokenInfo, err := jwt.ParseWithClaims(token, claims, func(_ *jwt.Token) (interface{}, error) { + return []byte(hmacSecret), nil + }) + + require.NoError(t, err) + assert.True(t, tokenInfo.Valid) + assert.Equal(t, tc.opts.OrgID.String(), claims.OrgID) + assert.Equal(t, tc.opts.OrgName, claims.OrgName) + assert.Equal(t, tc.opts.KeyID.String(), claims.ID) + assert.Equal(t, tc.opts.KeyName, claims.KeyName) + + if tc.opts.ProjectID != nil { + assert.Equal(t, tc.opts.ProjectID.String(), claims.ProjectID) + assert.Equal(t, *tc.opts.ProjectName, claims.ProjectName) + } else { + assert.Empty(t, claims.ProjectID) + assert.Empty(t, claims.ProjectName) + } - b, err := NewBuilder(WithIssuer("my-issuer"), WithKeySecret(hmacSecret)) - require.NoError(t, err) - - token, err := b.GenerateJWT("org-id", "org-name", "key-id", toPtrTime(time.Now().Add(1*time.Hour))) - assert.NoError(t, err) - assert.NotEmpty(t, token) - - // Verify signature and check claims - claims := &CustomClaims{} - tokenInfo, err := jwt.ParseWithClaims(token, claims, func(_ *jwt.Token) (interface{}, error) { - return []byte(hmacSecret), nil - }) - - require.NoError(t, err) - assert.True(t, tokenInfo.Valid) - assert.Equal(t, "org-id", claims.OrgID) - assert.Equal(t, "org-name", claims.OrgName) - assert.Equal(t, "key-id", claims.ID) - assert.Equal(t, "my-issuer", claims.Issuer) - assert.Contains(t, claims.Audience, Audience) - assert.NotNil(t, claims.ExpiresAt) + if tc.opts.ExpiresAt != nil { + assert.True(t, claims.ExpiresAt.After(time.Now())) + } else { + assert.Nil(t, claims.ExpiresAt) + } + }) + } } -func toPtrTime(t time.Time) *time.Time { +func toPtr[T any](t T) *T { return &t } From 6f003c647440f491f9f95dc5041e185594b6d919 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 22:26:37 +0200 Subject: [PATCH 10/13] feat: bundle authorization Signed-off-by: Miguel Martinez --- app/controlplane/internal/service/service.go | 40 +++++++++++++++++-- app/controlplane/internal/service/workflow.go | 2 +- .../usercontext/apitoken_middleware.go | 10 ++++- .../currentorganization_middleware.go | 3 +- .../internal/usercontext/entities/apitoken.go | 14 +++++-- .../usercontext/entities/memberships.go | 2 + 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index abe0e4d20..39710b36a 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -163,18 +163,35 @@ func (s *service) authorizeResource(ctx context.Context, op *authz.Policy, resou return nil } - // Apply RBAC + // 1 - Authorize using API token + // For now we only support API tokens to authorize project resourceTypes + // NOTE we do not run s.enforcer here because API tokens do not have roles associated with resourceTypes + // the authorization has happened at the API level and we do not have attribute-based policies in casbin yet + if token := entities.CurrentAPIToken(ctx); token != nil { + if resourceType == authz.ResourceTypeProject && token.ProjectID != nil && token.ProjectID.String() == resourceID.String() { + s.log.Debugw("msg", "authorized using API token", "resource_id", resourceID.String(), "resource_type", resourceType, "token_name", token.Name, "token_id", token.ID) + return nil + } + + return errors.Forbidden("forbidden", "operation not allowed") + } + + // 2 - We are a user + // find the resource membership that matches the resource type and ID + // for example admin in project1, then apply RBAC enforcement m := entities.CurrentMembership(ctx) - // check for specific resource role for _, rm := range m.Resources { if rm.ResourceType == resourceType && rm.ResourceID == resourceID { pass, err := s.enforcer.Enforce(string(rm.Role), op) if err != nil { return handleUseCaseErr(err, s.log) } + if !pass { return errors.Forbidden("forbidden", "operation not allowed") } + + s.log.Debugw("msg", "authorized using user membership", "resource_id", resourceID.String(), "resource_type", resourceType, "role", rm.Role, "membership_id", rm.MembershipID, "user_id", m.UserID) return nil } } @@ -212,6 +229,15 @@ func (s *service) visibleProjects(ctx context.Context) []uuid.UUID { projects := make([]uuid.UUID, 0) + // 1 - Check if we are using an API token + if token := entities.CurrentAPIToken(ctx); token != nil { + if token.ProjectID != nil { + projects = append(projects, *token.ProjectID) + } + return projects + } + + // 2 - We are a user m := entities.CurrentMembership(ctx) for _, rm := range m.Resources { if rm.ResourceType == authz.ResourceTypeProject { @@ -222,8 +248,16 @@ func (s *service) visibleProjects(ctx context.Context) []uuid.UUID { return projects } -// RBAC feature is enabled if the user has the `Org Member` role. +// RBAC feature is enabled if we are using a project scoped token or +// it is a user with org role member func rbacEnabled(ctx context.Context) bool { + // it's an API token + token := entities.CurrentAPIToken(ctx) + if token != nil { + return token.ProjectID != nil + } + + // we have an user currentSubject := usercontext.CurrentAuthzSubject(ctx) return currentSubject == string(authz.RoleOrgMember) } diff --git a/app/controlplane/internal/service/workflow.go b/app/controlplane/internal/service/workflow.go index 34fedbebd..298304cc7 100644 --- a/app/controlplane/internal/service/workflow.go +++ b/app/controlplane/internal/service/workflow.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/app/controlplane/internal/usercontext/apitoken_middleware.go b/app/controlplane/internal/usercontext/apitoken_middleware.go index 226aa4b01..a6a8b6a85 100644 --- a/app/controlplane/internal/usercontext/apitoken_middleware.go +++ b/app/controlplane/internal/usercontext/apitoken_middleware.go @@ -186,7 +186,15 @@ func setCurrentOrgAndAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUseCa // Set the current organization and API-Token in the context ctx = entities.WithCurrentOrg(ctx, &entities.Org{Name: org.Name, ID: org.ID, CreatedAt: org.CreatedAt}) - ctx = entities.WithCurrentAPIToken(ctx, &entities.APIToken{ID: token.ID.String(), CreatedAt: token.CreatedAt, Token: token.JWT}) + + ctx = entities.WithCurrentAPIToken(ctx, &entities.APIToken{ + ID: token.ID.String(), + Name: token.Name, + CreatedAt: token.CreatedAt, + Token: token.JWT, + ProjectID: token.ProjectID, + ProjectName: token.ProjectName, + }) // Set the authorization subject that will be used to check the policies subjectAPIToken := authz.SubjectAPIToken{ID: token.ID.String()} diff --git a/app/controlplane/internal/usercontext/currentorganization_middleware.go b/app/controlplane/internal/usercontext/currentorganization_middleware.go index 394790395..94f54c18a 100644 --- a/app/controlplane/internal/usercontext/currentorganization_middleware.go +++ b/app/controlplane/internal/usercontext/currentorganization_middleware.go @@ -105,10 +105,11 @@ func setCurrentMembershipsForUser(ctx context.Context, u *entities.User, members Role: m.Role, ResourceType: m.ResourceType, ResourceID: m.ResourceID, + MembershipID: m.ID, }) } - membership = &entities.Membership{Resources: resourceMemberships} + membership = &entities.Membership{UserID: uuid.MustParse(u.ID), Resources: resourceMemberships} membershipsCache.Add(u.ID, membership) } diff --git a/app/controlplane/internal/usercontext/entities/apitoken.go b/app/controlplane/internal/usercontext/entities/apitoken.go index 5ff78174c..efdab169e 100644 --- a/app/controlplane/internal/usercontext/entities/apitoken.go +++ b/app/controlplane/internal/usercontext/entities/apitoken.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,12 +18,18 @@ package entities import ( "context" "time" + + "github.com/google/uuid" ) type APIToken struct { - ID string - CreatedAt *time.Time - Token string + ID string + // Token Name + Name string + CreatedAt *time.Time + Token string + ProjectID *uuid.UUID + ProjectName *string } func WithCurrentAPIToken(ctx context.Context, token *APIToken) context.Context { diff --git a/app/controlplane/internal/usercontext/entities/memberships.go b/app/controlplane/internal/usercontext/entities/memberships.go index b8cd9cc9c..042f78559 100644 --- a/app/controlplane/internal/usercontext/entities/memberships.go +++ b/app/controlplane/internal/usercontext/entities/memberships.go @@ -23,10 +23,12 @@ import ( ) type Membership struct { + UserID uuid.UUID Resources []*ResourceMembership } type ResourceMembership struct { + MembershipID uuid.UUID Role authz.Role ResourceType authz.ResourceType ResourceID uuid.UUID From 83f2bb0d03251c9fef5092630bcb5338b9d38f67 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 22:32:51 +0200 Subject: [PATCH 11/13] feat: bundle authorization Signed-off-by: Miguel Martinez --- app/controlplane/internal/service/service.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index 39710b36a..e60a7e9f3 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -17,6 +17,7 @@ package service import ( "context" + "fmt" "io" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext" @@ -173,7 +174,7 @@ func (s *service) authorizeResource(ctx context.Context, op *authz.Policy, resou return nil } - return errors.Forbidden("forbidden", "operation not allowed") + return errors.Forbidden("forbidden", fmt.Errorf("operation not allowed: This auth token is valid only with the project %q", *token.ProjectName).Error()) } // 2 - We are a user From 34cebb92481b44ad7c0b7924749ab3eed9d81e1e Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 26 Jun 2025 23:22:24 +0200 Subject: [PATCH 12/13] fix tests Signed-off-by: Miguel Martinez --- .../pkg/biz/apitoken_integration_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controlplane/pkg/biz/apitoken_integration_test.go b/app/controlplane/pkg/biz/apitoken_integration_test.go index 666a1dfce..ba29a5bf4 100644 --- a/app/controlplane/pkg/biz/apitoken_integration_test.go +++ b/app/controlplane/pkg/biz/apitoken_integration_test.go @@ -78,7 +78,7 @@ func (s *apiTokenTestSuite) TestCreate() { name string tokenName string wantErrMsg string - projectID *uuid.UUID + project *biz.Project }{ { name: "name missing", @@ -107,17 +107,17 @@ func (s *apiTokenTestSuite) TestCreate() { { name: "tokens in projects can have the same name", tokenName: "my-name", - projectID: &s.p1.ID, + project: s.p1, }, { name: "tokens in different projects too", tokenName: "my-name", - projectID: &s.p2.ID, + project: s.p2, }, { name: "can't be duplicated in the same project", tokenName: "my-name", - projectID: &s.p1.ID, + project: s.p1, wantErrMsg: "name already taken", }, } @@ -125,8 +125,8 @@ func (s *apiTokenTestSuite) TestCreate() { for _, tc := range testCases { s.Run(tc.name, func() { var opts []biz.APITokenUseCaseOpt - if tc.projectID != nil { - opts = append(opts, biz.APITokenWithProject(s.p1)) + if tc.project != nil { + opts = append(opts, biz.APITokenWithProject(tc.project)) } token, err := s.APIToken.Create(ctx, tc.tokenName, nil, nil, s.org.ID, opts...) From 2b9a44b169639e0bcd0f3f84df342c111bb307ed Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Fri, 27 Jun 2025 15:18:47 +0200 Subject: [PATCH 13/13] fix tests Signed-off-by: Miguel Martinez --- .../internal/usercontext/apitoken_middleware.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controlplane/internal/usercontext/apitoken_middleware.go b/app/controlplane/internal/usercontext/apitoken_middleware.go index a6a8b6a85..eb5f1b46a 100644 --- a/app/controlplane/internal/usercontext/apitoken_middleware.go +++ b/app/controlplane/internal/usercontext/apitoken_middleware.go @@ -71,12 +71,15 @@ func WithCurrentAPITokenAndOrgMiddleware(apiTokenUC *biz.APITokenUseCase, orgUC return nil, errors.New("error mapping the API-token claims") } - ctx, err = setCurrentOrgAndAPIToken(ctx, apiTokenUC, orgUC, tokenID) + // Project ID is optional + projectID, _ := genericClaims["project_id"].(string) + + ctx, err = setCurrentOrgAndAPIToken(ctx, apiTokenUC, orgUC, tokenID, projectID) if err != nil { return nil, fmt.Errorf("error setting current org and user: %w", err) } - logger.Infow("msg", "[authN] processed credentials", "id", tokenID, "type", "API-token") + logger.Infow("msg", "[authN] processed credentials", "id", tokenID, "type", "API-token", "projectID", projectID) } return handler(ctx, req) @@ -120,7 +123,7 @@ func WithAttestationContextFromAPIToken(apiTokenUC *biz.APITokenUseCase, orgUC * return nil, fmt.Errorf("error extracting organization from APIToken: %w", err) } - ctx, err = setCurrentOrgAndAPIToken(ctx, apiTokenUC, orgUC, tokenID) + ctx, err = setCurrentOrgAndAPIToken(ctx, apiTokenUC, orgUC, tokenID, claims.ProjectID) if err != nil { return nil, fmt.Errorf("error setting current org and user: %w", err) } @@ -157,7 +160,7 @@ func setRobotAccountFromAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUs } // Set the current organization and API-Token in the context -func setCurrentOrgAndAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUseCase, orgUC *biz.OrganizationUseCase, tokenID string) (context.Context, error) { +func setCurrentOrgAndAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUseCase, orgUC *biz.OrganizationUseCase, tokenID, projectIDInClaim string) (context.Context, error) { if tokenID == "" { return nil, errors.New("error retrieving the key ID from the API token") } @@ -170,6 +173,11 @@ func setCurrentOrgAndAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUseCa return nil, errors.New("API token not found") } + // Make sure that the projectID that comes in the token claim matches the one in the DB + if projectIDInClaim != "" && token.ProjectID.String() != projectIDInClaim { + return nil, errors.New("API token project mismatch") + } + // Note: Expiration time does not need to be checked because that's done at the JWT // verification layer, which happens before this middleware is called if token.RevokedAt != nil {