diff --git a/cmd/dex/config.go b/cmd/dex/config.go index aa49a18188..1f655d0a3e 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -365,9 +365,10 @@ func (s *Storage) UnmarshalJSON(b []byte) error { // Connector is a magical type that can unmarshal YAML dynamically. The // Type field determines the connector type, which is then customized for Config. type Connector struct { - Type string `json:"type"` - Name string `json:"name"` - ID string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + ID string `json:"id"` + Hidden bool `json:"hidden"` Config server.ConnectorConfig `json:"config"` } @@ -376,9 +377,10 @@ type Connector struct { // dynamically determine the type of the connector config. func (c *Connector) UnmarshalJSON(b []byte) error { var conn struct { - Type string `json:"type"` - Name string `json:"name"` - ID string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + ID string `json:"id"` + Hidden bool `json:"hidden"` Config json.RawMessage `json:"config"` } @@ -422,6 +424,7 @@ func (c *Connector) UnmarshalJSON(b []byte) error { Name: conn.Name, ID: conn.ID, Config: connConfig, + Hidden: conn.Hidden, } return nil } @@ -438,6 +441,7 @@ func ToStorageConnector(c Connector) (storage.Connector, error) { Type: c.Type, Name: c.Name, Config: data, + Hidden: c.Hidden, }, nil } diff --git a/server/handlers.go b/server/handlers.go index f8d0ed64c3..e6de12972c 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -183,10 +183,11 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) { for index, conn := range connectors { connURL.Path = s.absPath("/auth", url.PathEscape(conn.ID)) connectorInfos[index] = connectorInfo{ - ID: conn.ID, - Name: conn.Name, - Type: conn.Type, - URL: template.URL(connURL.String()), + ID: conn.ID, + Name: conn.Name, + Type: conn.Type, + URL: template.URL(connURL.String()), + Hidden: conn.Hidden, } } diff --git a/server/templates.go b/server/templates.go index b77663e1f5..bb00c0dd6e 100644 --- a/server/templates.go +++ b/server/templates.go @@ -249,10 +249,11 @@ var scopeDescriptions = map[string]string{ } type connectorInfo struct { - ID string - Name string - URL template.URL - Type string + ID string + Name string + URL template.URL + Type string + Hidden bool } type byName []connectorInfo @@ -283,11 +284,17 @@ func (t *templates) deviceSuccess(r *http.Request, w http.ResponseWriter, client } func (t *templates) login(r *http.Request, w http.ResponseWriter, connectors []connectorInfo) error { - sort.Sort(byName(connectors)) + var visibleConnectors []connectorInfo + for _, connector := range connectors { + if !connector.Hidden { + visibleConnectors = append(visibleConnectors, connector) + } + } + sort.Sort(byName(visibleConnectors)) data := struct { Connectors []connectorInfo ReqPath string - }{connectors, r.URL.Path} + }{visibleConnectors, r.URL.Path} return renderTemplate(w, t.loginTmpl, data) } diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index f9d219619d..aa1b8a56e0 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -622,6 +622,7 @@ func testConnectorCRUD(t *testing.T, s storage.Storage) { Type: "Default", Name: "Default", Config: config1, + Hidden: false, } if err := s.CreateConnector(ctx, c1); err != nil { @@ -639,6 +640,7 @@ func testConnectorCRUD(t *testing.T, s storage.Storage) { Type: "Mock", Name: "Mock", Config: config2, + Hidden: false, } if err := s.CreateConnector(ctx, c2); err != nil { diff --git a/storage/ent/client/connector.go b/storage/ent/client/connector.go index f0cff8ba6a..3cf54c3d84 100644 --- a/storage/ent/client/connector.go +++ b/storage/ent/client/connector.go @@ -14,6 +14,7 @@ func (d *Database) CreateConnector(ctx context.Context, connector storage.Connec SetType(connector.Type). SetResourceVersion(connector.ResourceVersion). SetConfig(connector.Config). + SetHidden(connector.Hidden). Save(ctx) if err != nil { return convertDBError("create connector: %w", err) diff --git a/storage/ent/db/connector.go b/storage/ent/db/connector.go index 2071d14ebb..8efeb634cd 100644 --- a/storage/ent/db/connector.go +++ b/storage/ent/db/connector.go @@ -23,7 +23,9 @@ type Connector struct { // ResourceVersion holds the value of the "resource_version" field. ResourceVersion string `json:"resource_version,omitempty"` // Config holds the value of the "config" field. - Config []byte `json:"config,omitempty"` + Config []byte `json:"config,omitempty"` + // Hidden holds the value of the "hidden" field. + Hidden bool `json:"hidden,omitempty"` selectValues sql.SelectValues } @@ -34,6 +36,8 @@ func (*Connector) scanValues(columns []string) ([]any, error) { switch columns[i] { case connector.FieldConfig: values[i] = new([]byte) + case connector.FieldHidden: + values[i] = new(sql.NullBool) case connector.FieldID, connector.FieldType, connector.FieldName, connector.FieldResourceVersion: values[i] = new(sql.NullString) default: @@ -81,6 +85,12 @@ func (_m *Connector) assignValues(columns []string, values []any) error { } else if value != nil { _m.Config = *value } + case connector.FieldHidden: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field hidden", values[i]) + } else if value.Valid { + _m.Hidden = value.Bool + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -128,6 +138,9 @@ func (_m *Connector) String() string { builder.WriteString(", ") builder.WriteString("config=") builder.WriteString(fmt.Sprintf("%v", _m.Config)) + builder.WriteString(", ") + builder.WriteString("hidden=") + builder.WriteString(fmt.Sprintf("%v", _m.Hidden)) builder.WriteByte(')') return builder.String() } diff --git a/storage/ent/db/connector/connector.go b/storage/ent/db/connector/connector.go index 996328c12e..332172dea9 100644 --- a/storage/ent/db/connector/connector.go +++ b/storage/ent/db/connector/connector.go @@ -19,6 +19,8 @@ const ( FieldResourceVersion = "resource_version" // FieldConfig holds the string denoting the config field in the database. FieldConfig = "config" + // FieldHidden holds the string denoting the hidden field in the database. + FieldHidden = "hidden" // Table holds the table name of the connector in the database. Table = "connectors" ) @@ -30,6 +32,7 @@ var Columns = []string{ FieldName, FieldResourceVersion, FieldConfig, + FieldHidden, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -47,6 +50,8 @@ var ( TypeValidator func(string) error // NameValidator is a validator for the "name" field. It is called by the builders before save. NameValidator func(string) error + // DefaultHidden holds the default value on creation for the "hidden" field. + DefaultHidden bool // IDValidator is a validator for the "id" field. It is called by the builders before save. IDValidator func(string) error ) @@ -73,3 +78,8 @@ func ByName(opts ...sql.OrderTermOption) OrderOption { func ByResourceVersion(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldResourceVersion, opts...).ToFunc() } + +// ByHidden orders the results by the hidden field. +func ByHidden(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldHidden, opts...).ToFunc() +} diff --git a/storage/ent/db/connector/where.go b/storage/ent/db/connector/where.go index 39cc477fce..c34b63e310 100644 --- a/storage/ent/db/connector/where.go +++ b/storage/ent/db/connector/where.go @@ -82,6 +82,11 @@ func Config(v []byte) predicate.Connector { return predicate.Connector(sql.FieldEQ(FieldConfig, v)) } +// Hidden applies equality check predicate on the "hidden" field. It's identical to HiddenEQ. +func Hidden(v bool) predicate.Connector { + return predicate.Connector(sql.FieldEQ(FieldHidden, v)) +} + // TypeEQ applies the EQ predicate on the "type" field. func TypeEQ(v string) predicate.Connector { return predicate.Connector(sql.FieldEQ(FieldType, v)) @@ -317,6 +322,16 @@ func ConfigLTE(v []byte) predicate.Connector { return predicate.Connector(sql.FieldLTE(FieldConfig, v)) } +// HiddenEQ applies the EQ predicate on the "hidden" field. +func HiddenEQ(v bool) predicate.Connector { + return predicate.Connector(sql.FieldEQ(FieldHidden, v)) +} + +// HiddenNEQ applies the NEQ predicate on the "hidden" field. +func HiddenNEQ(v bool) predicate.Connector { + return predicate.Connector(sql.FieldNEQ(FieldHidden, v)) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Connector) predicate.Connector { return predicate.Connector(sql.AndPredicates(predicates...)) diff --git a/storage/ent/db/connector_create.go b/storage/ent/db/connector_create.go index 42da769e70..9a62c488e0 100644 --- a/storage/ent/db/connector_create.go +++ b/storage/ent/db/connector_create.go @@ -43,6 +43,20 @@ func (_c *ConnectorCreate) SetConfig(v []byte) *ConnectorCreate { return _c } +// SetHidden sets the "hidden" field. +func (_c *ConnectorCreate) SetHidden(v bool) *ConnectorCreate { + _c.mutation.SetHidden(v) + return _c +} + +// SetNillableHidden sets the "hidden" field if the given value is not nil. +func (_c *ConnectorCreate) SetNillableHidden(v *bool) *ConnectorCreate { + if v != nil { + _c.SetHidden(*v) + } + return _c +} + // SetID sets the "id" field. func (_c *ConnectorCreate) SetID(v string) *ConnectorCreate { _c.mutation.SetID(v) @@ -56,6 +70,7 @@ func (_c *ConnectorCreate) Mutation() *ConnectorMutation { // Save creates the Connector in the database. func (_c *ConnectorCreate) Save(ctx context.Context) (*Connector, error) { + _c.defaults() return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) } @@ -81,6 +96,14 @@ func (_c *ConnectorCreate) ExecX(ctx context.Context) { } } +// defaults sets the default values of the builder before save. +func (_c *ConnectorCreate) defaults() { + if _, ok := _c.mutation.Hidden(); !ok { + v := connector.DefaultHidden + _c.mutation.SetHidden(v) + } +} + // check runs all checks and user-defined validators on the builder. func (_c *ConnectorCreate) check() error { if _, ok := _c.mutation.GetType(); !ok { @@ -105,6 +128,9 @@ func (_c *ConnectorCreate) check() error { if _, ok := _c.mutation.Config(); !ok { return &ValidationError{Name: "config", err: errors.New(`db: missing required field "Connector.config"`)} } + if _, ok := _c.mutation.Hidden(); !ok { + return &ValidationError{Name: "hidden", err: errors.New(`db: missing required field "Connector.hidden"`)} + } if v, ok := _c.mutation.ID(); ok { if err := connector.IDValidator(v); err != nil { return &ValidationError{Name: "id", err: fmt.Errorf(`db: validator failed for field "Connector.id": %w`, err)} @@ -161,6 +187,10 @@ func (_c *ConnectorCreate) createSpec() (*Connector, *sqlgraph.CreateSpec) { _spec.SetField(connector.FieldConfig, field.TypeBytes, value) _node.Config = value } + if value, ok := _c.mutation.Hidden(); ok { + _spec.SetField(connector.FieldHidden, field.TypeBool, value) + _node.Hidden = value + } return _node, _spec } @@ -182,6 +212,7 @@ func (_c *ConnectorCreateBulk) Save(ctx context.Context) ([]*Connector, error) { for i := range _c.builders { func(i int, root context.Context) { builder := _c.builders[i] + builder.defaults() var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { mutation, ok := m.(*ConnectorMutation) if !ok { diff --git a/storage/ent/db/connector_update.go b/storage/ent/db/connector_update.go index bbcfd5654c..3a373bf7b8 100644 --- a/storage/ent/db/connector_update.go +++ b/storage/ent/db/connector_update.go @@ -75,6 +75,20 @@ func (_u *ConnectorUpdate) SetConfig(v []byte) *ConnectorUpdate { return _u } +// SetHidden sets the "hidden" field. +func (_u *ConnectorUpdate) SetHidden(v bool) *ConnectorUpdate { + _u.mutation.SetHidden(v) + return _u +} + +// SetNillableHidden sets the "hidden" field if the given value is not nil. +func (_u *ConnectorUpdate) SetNillableHidden(v *bool) *ConnectorUpdate { + if v != nil { + _u.SetHidden(*v) + } + return _u +} + // Mutation returns the ConnectorMutation object of the builder. func (_u *ConnectorUpdate) Mutation() *ConnectorMutation { return _u.mutation @@ -146,6 +160,9 @@ func (_u *ConnectorUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.Config(); ok { _spec.SetField(connector.FieldConfig, field.TypeBytes, value) } + if value, ok := _u.mutation.Hidden(); ok { + _spec.SetField(connector.FieldHidden, field.TypeBool, value) + } if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{connector.Label} @@ -214,6 +231,20 @@ func (_u *ConnectorUpdateOne) SetConfig(v []byte) *ConnectorUpdateOne { return _u } +// SetHidden sets the "hidden" field. +func (_u *ConnectorUpdateOne) SetHidden(v bool) *ConnectorUpdateOne { + _u.mutation.SetHidden(v) + return _u +} + +// SetNillableHidden sets the "hidden" field if the given value is not nil. +func (_u *ConnectorUpdateOne) SetNillableHidden(v *bool) *ConnectorUpdateOne { + if v != nil { + _u.SetHidden(*v) + } + return _u +} + // Mutation returns the ConnectorMutation object of the builder. func (_u *ConnectorUpdateOne) Mutation() *ConnectorMutation { return _u.mutation @@ -315,6 +346,9 @@ func (_u *ConnectorUpdateOne) sqlSave(ctx context.Context) (_node *Connector, er if value, ok := _u.mutation.Config(); ok { _spec.SetField(connector.FieldConfig, field.TypeBytes, value) } + if value, ok := _u.mutation.Hidden(); ok { + _spec.SetField(connector.FieldHidden, field.TypeBool, value) + } _node = &Connector{config: _u.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/storage/ent/db/migrate/schema.go b/storage/ent/db/migrate/schema.go index d3295a0c79..798f722b80 100644 --- a/storage/ent/db/migrate/schema.go +++ b/storage/ent/db/migrate/schema.go @@ -70,6 +70,7 @@ var ( {Name: "name", Type: field.TypeString, Size: 2147483647, SchemaType: map[string]string{"mysql": "varchar(384)", "postgres": "text", "sqlite3": "text"}}, {Name: "resource_version", Type: field.TypeString, Size: 2147483647, SchemaType: map[string]string{"mysql": "varchar(384)", "postgres": "text", "sqlite3": "text"}}, {Name: "config", Type: field.TypeBytes}, + {Name: "hidden", Type: field.TypeBool, Default: false}, } // ConnectorsTable holds the schema information for the "connectors" table. ConnectorsTable = &schema.Table{ diff --git a/storage/ent/db/mutation.go b/storage/ent/db/mutation.go index 71203574e6..49b95851a8 100644 --- a/storage/ent/db/mutation.go +++ b/storage/ent/db/mutation.go @@ -2727,6 +2727,7 @@ type ConnectorMutation struct { name *string resource_version *string _config *[]byte + hidden *bool clearedFields map[string]struct{} done bool oldValue func(context.Context) (*Connector, error) @@ -2981,6 +2982,42 @@ func (m *ConnectorMutation) ResetConfig() { m._config = nil } +// SetHidden sets the "hidden" field. +func (m *ConnectorMutation) SetHidden(b bool) { + m.hidden = &b +} + +// Hidden returns the value of the "hidden" field in the mutation. +func (m *ConnectorMutation) Hidden() (r bool, exists bool) { + v := m.hidden + if v == nil { + return + } + return *v, true +} + +// OldHidden returns the old "hidden" field's value of the Connector entity. +// If the Connector 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 *ConnectorMutation) OldHidden(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldHidden is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldHidden requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldHidden: %w", err) + } + return oldValue.Hidden, nil +} + +// ResetHidden resets all changes to the "hidden" field. +func (m *ConnectorMutation) ResetHidden() { + m.hidden = nil +} + // Where appends a list predicates to the ConnectorMutation builder. func (m *ConnectorMutation) Where(ps ...predicate.Connector) { m.predicates = append(m.predicates, ps...) @@ -3015,7 +3052,7 @@ func (m *ConnectorMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ConnectorMutation) Fields() []string { - fields := make([]string, 0, 4) + fields := make([]string, 0, 5) if m._type != nil { fields = append(fields, connector.FieldType) } @@ -3028,6 +3065,9 @@ func (m *ConnectorMutation) Fields() []string { if m._config != nil { fields = append(fields, connector.FieldConfig) } + if m.hidden != nil { + fields = append(fields, connector.FieldHidden) + } return fields } @@ -3044,6 +3084,8 @@ func (m *ConnectorMutation) Field(name string) (ent.Value, bool) { return m.ResourceVersion() case connector.FieldConfig: return m.Config() + case connector.FieldHidden: + return m.Hidden() } return nil, false } @@ -3061,6 +3103,8 @@ func (m *ConnectorMutation) OldField(ctx context.Context, name string) (ent.Valu return m.OldResourceVersion(ctx) case connector.FieldConfig: return m.OldConfig(ctx) + case connector.FieldHidden: + return m.OldHidden(ctx) } return nil, fmt.Errorf("unknown Connector field %s", name) } @@ -3098,6 +3142,13 @@ func (m *ConnectorMutation) SetField(name string, value ent.Value) error { } m.SetConfig(v) return nil + case connector.FieldHidden: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetHidden(v) + return nil } return fmt.Errorf("unknown Connector field %s", name) } @@ -3159,6 +3210,9 @@ func (m *ConnectorMutation) ResetField(name string) error { case connector.FieldConfig: m.ResetConfig() return nil + case connector.FieldHidden: + m.ResetHidden() + return nil } return fmt.Errorf("unknown Connector field %s", name) } diff --git a/storage/ent/db/runtime.go b/storage/ent/db/runtime.go index 797c97613b..513bc58197 100644 --- a/storage/ent/db/runtime.go +++ b/storage/ent/db/runtime.go @@ -96,6 +96,10 @@ func init() { connectorDescName := connectorFields[2].Descriptor() // connector.NameValidator is a validator for the "name" field. It is called by the builders before save. connector.NameValidator = connectorDescName.Validators[0].(func(string) error) + // connectorDescHidden is the schema descriptor for hidden field. + connectorDescHidden := connectorFields[5].Descriptor() + // connector.DefaultHidden holds the default value on creation for the hidden field. + connector.DefaultHidden = connectorDescHidden.Default.(bool) // connectorDescID is the schema descriptor for id field. connectorDescID := connectorFields[0].Descriptor() // connector.IDValidator is a validator for the "id" field. It is called by the builders before save. diff --git a/storage/ent/schema/connector.go b/storage/ent/schema/connector.go index 41b65eb4fb..58d118721a 100644 --- a/storage/ent/schema/connector.go +++ b/storage/ent/schema/connector.go @@ -13,6 +13,7 @@ create table connector name text not null, resource_version text not null, config blob + hidden boolean default false not null ); */ @@ -38,6 +39,8 @@ func (Connector) Fields() []ent.Field { field.Text("resource_version"). SchemaType(textSchema), field.Bytes("config"), + field.Bool("hidden"). + Default(false), } } diff --git a/storage/storage.go b/storage/storage.go index 574b0a5a5e..32aa7ef878 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -374,6 +374,8 @@ type Connector struct { // However, fixing this requires migrating Kubernetes objects for all previously created connectors, // or making Dex reading both tags and act accordingly. Config []byte `json:"email"` + // It specifies if the connector should be hidden in the login web page. + Hidden bool `json:"hidden"` } // VerificationKey is a rotated signing key which can still be used to verify