snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
251
ui/node/attributes_input.go
Normal file
251
ui/node/attributes_input.go
Normal file
@@ -0,0 +1,251 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ory/kratos/text"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/x/jsonschemax"
|
||||
)
|
||||
|
||||
const DisableFormField = "disableFormField"
|
||||
|
||||
// maxEnumOptions caps how many `enum` values a single identity-schema
|
||||
// property may surface as a selectable option on a UI node. The cap bounds
|
||||
// the size of flow responses when an operator (accidentally or otherwise)
|
||||
// declares a very large enum, so a single misconfigured schema cannot
|
||||
// inflate every active registration/settings/login flow on a tenant.
|
||||
const maxEnumOptions = 500
|
||||
|
||||
func toFormType(n string, i interface{}) UiNodeInputAttributeType {
|
||||
switch n {
|
||||
case nosurfx.CSRFTokenName:
|
||||
return InputAttributeTypeHidden
|
||||
case "password":
|
||||
return InputAttributeTypePassword
|
||||
}
|
||||
|
||||
switch i.(type) {
|
||||
case float64, int64, int32, float32, json.Number:
|
||||
return InputAttributeTypeNumber
|
||||
case bool:
|
||||
return InputAttributeTypeCheckbox
|
||||
}
|
||||
|
||||
return InputAttributeTypeText
|
||||
}
|
||||
|
||||
type (
|
||||
InputAttributesModifier func(attributes *InputAttributes)
|
||||
InputAttributesModifiers []InputAttributesModifier
|
||||
)
|
||||
|
||||
func WithRequiredInputAttribute(a *InputAttributes) {
|
||||
a.Required = true
|
||||
}
|
||||
|
||||
func WithMaxLengthInputAttribute(maxLength int) func(a *InputAttributes) {
|
||||
return func(a *InputAttributes) {
|
||||
a.MaxLength = maxLength
|
||||
}
|
||||
}
|
||||
|
||||
func WithInputAttributes(f func(a *InputAttributes)) func(a *InputAttributes) {
|
||||
return func(a *InputAttributes) {
|
||||
f(a)
|
||||
}
|
||||
}
|
||||
|
||||
func applyInputAttributes(opts []InputAttributesModifier, attributes *InputAttributes) *InputAttributes {
|
||||
for _, f := range opts {
|
||||
f(attributes)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type (
|
||||
ImageAttributesModifier func(attributes *ImageAttributes)
|
||||
ImageAttributesModifiers []ImageAttributesModifier
|
||||
)
|
||||
|
||||
func WithImageAttributes(f func(a *ImageAttributes)) func(a *ImageAttributes) {
|
||||
return func(a *ImageAttributes) {
|
||||
f(a)
|
||||
}
|
||||
}
|
||||
|
||||
func applyImageAttributes(opts ImageAttributesModifiers, attributes *ImageAttributes) *ImageAttributes {
|
||||
for _, f := range opts {
|
||||
f(attributes)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type (
|
||||
ScriptAttributesModifier func(attributes *ScriptAttributes)
|
||||
ScriptAttributesModifiers []ScriptAttributesModifier
|
||||
)
|
||||
|
||||
func applyScriptAttributes(opts ScriptAttributesModifiers, attributes *ScriptAttributes) *ScriptAttributes {
|
||||
for _, f := range opts {
|
||||
f(attributes)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
type (
|
||||
DivisionAttributesModifier func(attributes *DivisionAttributes)
|
||||
DivisionAttributesModifiers []DivisionAttributesModifier
|
||||
)
|
||||
|
||||
func WithDivisionAttributes(f func(a *DivisionAttributes)) func(a *DivisionAttributes) {
|
||||
return func(a *DivisionAttributes) {
|
||||
f(a)
|
||||
}
|
||||
}
|
||||
|
||||
func applyDivisionAttributes(opts DivisionAttributesModifiers, attributes *DivisionAttributes) *DivisionAttributes {
|
||||
for _, f := range opts {
|
||||
f(attributes)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
func NewInputFieldFromJSON(name string, value interface{}, group UiNodeGroup, opts ...InputAttributesModifier) *Node {
|
||||
return &Node{
|
||||
Type: Input,
|
||||
Group: group,
|
||||
Attributes: applyInputAttributes(opts, &InputAttributes{Name: name, Type: toFormType(name, value), FieldValue: value}),
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewInputField(name string, value interface{}, group UiNodeGroup, inputType UiNodeInputAttributeType, opts ...InputAttributesModifier) *Node {
|
||||
return &Node{
|
||||
Type: Input,
|
||||
Group: group,
|
||||
Attributes: applyInputAttributes(opts, &InputAttributes{Name: name, Type: inputType, FieldValue: value}),
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewImageField(id string, src string, group UiNodeGroup, opts ...ImageAttributesModifier) *Node {
|
||||
return &Node{
|
||||
Type: Image,
|
||||
Group: group,
|
||||
Attributes: applyImageAttributes(opts, &ImageAttributes{Source: src, Identifier: id}),
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewTextField(id string, text *text.Message, group UiNodeGroup) *Node {
|
||||
return &Node{
|
||||
Type: Text,
|
||||
Group: group,
|
||||
Attributes: &TextAttributes{Text: text, Identifier: id},
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewDivisionField(id string, group UiNodeGroup, opts ...DivisionAttributesModifier) *Node {
|
||||
return &Node{
|
||||
Type: Division,
|
||||
Group: group,
|
||||
Attributes: applyDivisionAttributes(opts, &DivisionAttributes{Identifier: id}),
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewAnchorField(id string, href string, group UiNodeGroup, title *text.Message) *Node {
|
||||
return &Node{
|
||||
Type: Anchor,
|
||||
Group: group,
|
||||
Attributes: &AnchorAttributes{Title: title, HREF: href, Identifier: id},
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewScriptField(name string, src string, group UiNodeGroup, integrity string, opts ...ScriptAttributesModifier) *Node {
|
||||
return &Node{
|
||||
Type: Script,
|
||||
Group: group,
|
||||
Attributes: applyScriptAttributes(opts, &ScriptAttributes{
|
||||
Identifier: name,
|
||||
Type: "text/javascript",
|
||||
Source: src,
|
||||
Async: true,
|
||||
ReferrerPolicy: "no-referrer",
|
||||
CrossOrigin: "anonymous",
|
||||
Integrity: integrity,
|
||||
Nonce: x.NewUUID().String(),
|
||||
}),
|
||||
Meta: &Meta{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewInputFieldFromSchema(name string, group UiNodeGroup, p jsonschemax.Path, opts ...InputAttributesModifier) *Node {
|
||||
attr := &InputAttributes{
|
||||
Name: name,
|
||||
Type: toFormType(p.Name, p.Type),
|
||||
Required: p.Required,
|
||||
}
|
||||
|
||||
// If format is set, we can make a more distinct decision:
|
||||
switch p.Format {
|
||||
case "date-time":
|
||||
attr.Type = InputAttributeTypeDateTimeLocal
|
||||
case "email":
|
||||
attr.Type = InputAttributeTypeEmail
|
||||
attr.Autocomplete = InputAttributeAutocompleteEmail
|
||||
case "tel":
|
||||
attr.Type = InputAttributeTypeTel
|
||||
attr.Autocomplete = InputAttributeAutocompleteTel
|
||||
case "date":
|
||||
attr.Type = InputAttributeTypeDate
|
||||
case "uri":
|
||||
attr.Type = InputAttributeTypeURI
|
||||
attr.Autocomplete = InputAttributeAutocompleteUrl
|
||||
case "regex":
|
||||
attr.Type = InputAttributeTypeText
|
||||
}
|
||||
|
||||
// Enum values become selectable options. The presence of options is the
|
||||
// signal for consumers to render a dropdown instead of a free-form input.
|
||||
// Cap the number of options to keep a pathological schema from inflating
|
||||
// every flow response.
|
||||
if len(p.Enum) > 0 {
|
||||
n := min(len(p.Enum), maxEnumOptions)
|
||||
attr.Options = make([]InputAttributesOption, n)
|
||||
for i, v := range p.Enum[:n] {
|
||||
attr.Options[i] = InputAttributesOption{Value: v}
|
||||
}
|
||||
}
|
||||
|
||||
// Other properties
|
||||
if p.Pattern != nil {
|
||||
attr.Pattern = p.Pattern.String()
|
||||
}
|
||||
|
||||
// Set disabled if the custom property is set
|
||||
if isDisabled, ok := p.CustomProperties[DisableFormField]; ok {
|
||||
if isDisabled, ok := isDisabled.(bool); ok {
|
||||
attr.Disabled = isDisabled
|
||||
}
|
||||
}
|
||||
|
||||
var meta Meta
|
||||
if len(p.Title) > 0 {
|
||||
meta.Label = text.NewInfoNodeLabelGenerated(p.Title, name)
|
||||
}
|
||||
|
||||
return &Node{
|
||||
Type: Input,
|
||||
Attributes: applyInputAttributes(opts, attr),
|
||||
Group: group,
|
||||
Meta: &meta,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user