mirror of
https://github.com/teamhanko/hanko.git
synced 2025-10-29 15:49:41 +08:00
This pull request introduces the new Flowpilot system along with several new features and various improvements. The key enhancements include configurable authorization, registration, and profile flows, as well as the ability to enable and disable user identifiers (e.g., email addresses and usernames) and login methods. --------- Co-authored-by: Frederic Jahn <frederic.jahn@hanko.io> Co-authored-by: Lennart Fleischmann <lennart.fleischmann@hanko.io> Co-authored-by: lfleischmann <67686424+lfleischmann@users.noreply.github.com> Co-authored-by: merlindru <hello@merlindru.com>
283 lines
6.8 KiB
Go
283 lines
6.8 KiB
Go
package flowpilot
|
|
|
|
import (
|
|
"regexp"
|
|
)
|
|
|
|
// inputType represents the type of the input field.
|
|
type inputType string
|
|
|
|
// Input types enumeration.
|
|
const (
|
|
inputTypeString inputType = "string"
|
|
inputTypeBoolean inputType = "boolean"
|
|
inputTypeEmail inputType = "email"
|
|
inputTypeNumber inputType = "number"
|
|
inputTypePassword inputType = "password"
|
|
inputTypeJSON inputType = "json"
|
|
)
|
|
|
|
// Input defines the interface for input fields.
|
|
type Input interface {
|
|
MinLength(minLength int) Input
|
|
MaxLength(maxLength int) Input
|
|
Required(b bool) Input
|
|
Hidden(b bool) Input
|
|
Preserve(b bool) Input
|
|
AllowedValue(name string, value interface{}) Input
|
|
TrimSpace(b bool) Input
|
|
LowerCase(b bool) Input
|
|
|
|
setValue(value interface{}) Input
|
|
setError(inputError InputError)
|
|
getError() InputError
|
|
getName() string
|
|
shouldPreserve() bool
|
|
shouldTrimSpace() bool
|
|
shouldConvertToLowerCase() bool
|
|
validate(inputData readOnlyActionInput) bool
|
|
toResponseInput() *ResponseInput
|
|
}
|
|
|
|
// defaultExtraInputOptions holds additional input field options.
|
|
type defaultExtraInputOptions struct {
|
|
preserveValue bool
|
|
trimSpace bool
|
|
lowerCase bool
|
|
}
|
|
|
|
// defaultInput represents an input field with its options.
|
|
type defaultInput struct {
|
|
name string
|
|
dataType inputType
|
|
value interface{}
|
|
minLength *int
|
|
maxLength *int
|
|
required *bool
|
|
hidden *bool
|
|
error InputError
|
|
allowedValues allowedValues
|
|
|
|
defaultExtraInputOptions
|
|
}
|
|
|
|
func (i *defaultInput) AllowedValue(name string, value interface{}) Input {
|
|
i.allowedValues.add(&defaultAllowedValue{
|
|
value: value,
|
|
text: name,
|
|
})
|
|
return i
|
|
}
|
|
|
|
// newInput creates a new input instance with provided parameters.
|
|
func newInput(name string, inputType inputType) Input {
|
|
extraOptions := defaultExtraInputOptions{
|
|
preserveValue: false,
|
|
trimSpace: false,
|
|
lowerCase: false,
|
|
}
|
|
|
|
return &defaultInput{
|
|
name: name,
|
|
dataType: inputType,
|
|
defaultExtraInputOptions: extraOptions,
|
|
allowedValues: &defaultAllowedValues{},
|
|
}
|
|
}
|
|
|
|
// StringInput creates a new input field of string type.
|
|
func StringInput(name string) Input {
|
|
return newInput(name, inputTypeString)
|
|
}
|
|
|
|
// EmailInput creates a new input field of email type.
|
|
func EmailInput(name string) Input {
|
|
return newInput(name, inputTypeEmail)
|
|
}
|
|
|
|
// NumberInput creates a new input field of number type.
|
|
func NumberInput(name string) Input {
|
|
return newInput(name, inputTypeNumber)
|
|
}
|
|
|
|
// BooleanInput creates a new input field of boolean type.
|
|
func BooleanInput(name string) Input {
|
|
return newInput(name, inputTypeBoolean)
|
|
}
|
|
|
|
// PasswordInput creates a new input field of password type.
|
|
func PasswordInput(name string) Input {
|
|
return newInput(name, inputTypePassword)
|
|
}
|
|
|
|
// JSONInput creates a new input field of JSON type.
|
|
func JSONInput(name string) Input {
|
|
return newInput(name, inputTypeJSON)
|
|
}
|
|
|
|
// MinLength sets the minimum length for the input field.
|
|
func (i *defaultInput) MinLength(minLength int) Input {
|
|
i.minLength = &minLength
|
|
return i
|
|
}
|
|
|
|
// MaxLength sets the maximum length for the input field.
|
|
func (i *defaultInput) MaxLength(maxLength int) Input {
|
|
i.maxLength = &maxLength
|
|
return i
|
|
}
|
|
|
|
// Required sets whether the input field is required.
|
|
func (i *defaultInput) Required(b bool) Input {
|
|
i.required = &b
|
|
return i
|
|
}
|
|
|
|
// Hidden sets whether the input field is hidden.
|
|
func (i *defaultInput) Hidden(b bool) Input {
|
|
i.hidden = &b
|
|
return i
|
|
}
|
|
|
|
// Preserve sets whether the input field value should be preserved, so that the value is included in the response
|
|
// instead of being blanked out.
|
|
func (i *defaultInput) Preserve(b bool) Input {
|
|
i.preserveValue = b
|
|
return i
|
|
}
|
|
|
|
// TrimSpace sets whether the leading and trailing whitespaces should be trimmed.
|
|
func (i *defaultInput) TrimSpace(b bool) Input {
|
|
i.trimSpace = b
|
|
return i
|
|
}
|
|
|
|
// LowerCase sets whether the value should be converted to lower case.
|
|
func (i *defaultInput) LowerCase(b bool) Input {
|
|
i.lowerCase = b
|
|
return i
|
|
}
|
|
|
|
// setValue sets the value for the input field for the current response.
|
|
func (i *defaultInput) setValue(value interface{}) Input {
|
|
i.value = &value
|
|
return i
|
|
}
|
|
|
|
// getName returns the name of the input field.
|
|
func (i *defaultInput) getName() string {
|
|
return i.name
|
|
}
|
|
|
|
// setError sets an error to the given input field.
|
|
func (i *defaultInput) setError(inputError InputError) {
|
|
i.error = inputError
|
|
}
|
|
|
|
// getError returns the input error.
|
|
func (i *defaultInput) getError() InputError {
|
|
return i.error
|
|
}
|
|
|
|
// shouldPreserve indicates the value should be preserved.
|
|
func (i *defaultInput) shouldPreserve() bool {
|
|
return i.preserveValue
|
|
}
|
|
|
|
// shouldTrimSpace indicates the value should be trimmed.
|
|
func (i *defaultInput) shouldTrimSpace() bool {
|
|
return i.trimSpace
|
|
}
|
|
|
|
// shouldConvertToLowerCase indicates the value should be converted to lower case.
|
|
func (i *defaultInput) shouldConvertToLowerCase() bool {
|
|
return i.lowerCase
|
|
}
|
|
|
|
// validate performs validation on the input field.
|
|
func (i *defaultInput) validate(inputData readOnlyActionInput) bool {
|
|
// TODO: Replace with more structured validation logic.
|
|
|
|
var inputValue *string
|
|
|
|
if v := inputData.Get(i.name); v.Exists() {
|
|
inputValue = &v.Str
|
|
}
|
|
|
|
if i.dataType == inputTypeJSON {
|
|
// skip further validation
|
|
return true
|
|
}
|
|
|
|
if i.dataType == inputTypeBoolean {
|
|
return true
|
|
}
|
|
|
|
isRequired := i.required != nil && *i.required
|
|
hasEmptyOrNilValue := inputValue == nil || len(*inputValue) <= 0
|
|
|
|
if isRequired && hasEmptyOrNilValue {
|
|
i.error = ErrorValueMissing
|
|
return false
|
|
}
|
|
|
|
if !hasEmptyOrNilValue && i.minLength != nil {
|
|
if len(*inputValue) < *i.minLength {
|
|
i.error = ErrorValueTooShort
|
|
return false
|
|
}
|
|
}
|
|
|
|
if !hasEmptyOrNilValue && i.maxLength != nil {
|
|
if len(*inputValue) > *i.maxLength {
|
|
i.error = ErrorValueTooLong
|
|
return false
|
|
}
|
|
}
|
|
|
|
if i.dataType == inputTypeEmail && (isRequired || (!isRequired && !hasEmptyOrNilValue)) {
|
|
pattern := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
|
if matched := pattern.MatchString(*inputValue); !matched {
|
|
i.error = ErrorValueInvalid
|
|
return false
|
|
}
|
|
}
|
|
|
|
if i.dataType == inputTypeString && inputValue != nil {
|
|
if i.allowedValues.isAllowed(*inputValue) {
|
|
return true
|
|
}
|
|
|
|
i.error = createMustBeOneOfError(i.allowedValues.getValues())
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// toResponseInput converts the defaultInput to a ResponseInput for public exposure.
|
|
func (i *defaultInput) toResponseInput() *ResponseInput {
|
|
var e *ResponseError
|
|
var av *ResponseAllowedValues
|
|
|
|
if i.error != nil {
|
|
e = i.error.toResponseError(true)
|
|
}
|
|
|
|
if i.allowedValues != nil && i.allowedValues.hasAny() {
|
|
av = i.allowedValues.toResponseAllowedValues()
|
|
}
|
|
|
|
return &ResponseInput{
|
|
Name: i.name,
|
|
Type: i.dataType,
|
|
Value: i.value,
|
|
MinLength: i.minLength,
|
|
MaxLength: i.maxLength,
|
|
Required: i.required,
|
|
Hidden: i.hidden,
|
|
Error: e,
|
|
AllowedValues: av,
|
|
}
|
|
}
|