fix: Improve duplicates check

This commit is contained in:
Myzel394 2024-07-29 22:48:49 +02:00
parent d5cb9c53e9
commit 56b661db72
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
6 changed files with 139 additions and 109 deletions

View File

@ -72,10 +72,32 @@ func (v PositiveNumberValue) CheckIsValid(value string) error {
return nil return nil
} }
var SimpleDuplicatesExtractor = func(value string) string {
return value
}
var ExtractKeyDuplicatesExtractor = func(separator string) func(string) string {
return func(value string) string {
splitted := strings.Split(value, separator)
if len(splitted) == 0 {
return ""
}
return splitted[0]
}
}
var DuplicatesAllowedExtractor func(string) string = nil
type ArrayValue struct { type ArrayValue struct {
SubValue Value SubValue Value
Separator string Separator string
AllowDuplicates bool // If this function is nil, no duplicate check is done
// (value) => Extracted value
// This is used to extract the value from the user input,
// because you may want to preprocess the value before checking for duplicates
DuplicatesExtractor *(func(string) string)
} }
func (v ArrayValue) GetTypeDescription() []string { func (v ArrayValue) GetTypeDescription() []string {
@ -89,21 +111,19 @@ func (v ArrayValue) GetTypeDescription() []string {
func (v ArrayValue) CheckIsValid(value string) error { func (v ArrayValue) CheckIsValid(value string) error {
values := strings.Split(value, v.Separator) values := strings.Split(value, v.Separator)
for _, subValue := range values { println(fmt.Sprintf("values: %v", values))
err := v.SubValue.CheckIsValid(subValue)
if err != nil { if v.DuplicatesExtractor != nil {
return err valuesOccurrences := SliceToMap(
} Map(values, *v.DuplicatesExtractor),
} 0,
)
if !v.AllowDuplicates {
valuesOccurrences := SliceToMap(values, 0)
// Only continue if there are actually duplicate values // Only continue if there are actually duplicate values
if len(values) != len(valuesOccurrences) { if len(values) != len(valuesOccurrences) {
for _, subValue := range values { for _, duplicateRawValue := range values {
valuesOccurrences[subValue]++ duplicateValue := (*v.DuplicatesExtractor)(duplicateRawValue)
valuesOccurrences[duplicateValue]++
} }
duplicateValues := FilterMapWhere(valuesOccurrences, func(_ string, value int) bool { duplicateValues := FilterMapWhere(valuesOccurrences, func(_ string, value int) bool {
@ -116,6 +136,15 @@ func (v ArrayValue) CheckIsValid(value string) error {
} }
} }
for _, subValue := range values {
err := v.SubValue.CheckIsValid(subValue)
if err != nil {
return err
}
}
return nil return nil
} }

View File

@ -127,7 +127,7 @@ type ArrayContainsDuplicatesError struct {
Duplicates []string Duplicates []string
} }
func (e ArrayContainsDuplicatesError) Error() string { func (e ArrayContainsDuplicatesError) Error() string {
return fmt.Sprintf("Remove the following duplicate values: %s", strings.Join(e.Duplicates, ",")) return fmt.Sprintf("The following values are duplicated: %s", strings.Join(e.Duplicates, ","))
} }
type PathDoesNotExistError struct{} type PathDoesNotExistError struct{}

View File

@ -72,7 +72,7 @@ func UserValue(separatorForMultiple string, enforceValues bool) Value {
return enumValues return enumValues
} else { } else {
return ArrayValue{ return ArrayValue{
AllowDuplicates: false, DuplicatesExtractor: &SimpleDuplicatesExtractor,
SubValue: enumValues, SubValue: enumValues,
Separator: separatorForMultiple, Separator: separatorForMultiple,
} }

View File

@ -0,0 +1,91 @@
package openssh
import (
"config-lsp/common"
"os/exec"
"strings"
)
var BooleanEnumValue = common.EnumValue{
EnforceValues: true,
Values: []string{"yes", "no"},
}
var plusMinuxCaretPrefixes = []common.Prefix{
{
Prefix: "+",
Meaning: "Append to the default set",
},
{
Prefix: "-",
Meaning: "Remove from the default set",
},
{
Prefix: "^",
Meaning: "Place at the head of the default set",
},
}
var ChannelTimeoutExtractor = common.ExtractKeyDuplicatesExtractor("=")
func PrefixPlusMinusCaret(values []string) common.PrefixWithMeaningValue {
return common.PrefixWithMeaningValue{
Prefixes: []common.Prefix{
{
Prefix: "+",
Meaning: "Append to the default set",
},
{
Prefix: "-",
Meaning: "Remove from the default set",
},
{
Prefix: "^",
Meaning: "Place at the head of the default set",
},
},
SubValue: common.ArrayValue{
Separator: ",",
DuplicatesExtractor: &common.SimpleDuplicatesExtractor,
SubValue: common.EnumValue{
Values: values,
},
},
}
}
var _cachedQueries map[string][]string = make(map[string][]string)
func queryValues(query string) ([]string, error) {
cmd := exec.Command("ssh", "-Q", query)
output, err := cmd.Output()
if err != nil {
return []string{}, err
}
return strings.Split(string(output), "\n"), nil
}
func QueryOpenSSHOptions(
query string,
) ([]string, error) {
var availableQueries []string
key := query
if _cachedQueries[key] != nil && len(_cachedQueries[key]) > 0 {
return _cachedQueries[key], nil
} else {
availableQueries, err := queryValues(query)
if err != nil {
return []string{}, err
}
_cachedQueries[key] = availableQueries
}
return availableQueries, nil
}

View File

@ -1,41 +0,0 @@
package openssh
import (
"os/exec"
"strings"
)
var _cachedQueries map[string][]string = make(map[string][]string)
func queryValues(query string) ([]string, error) {
cmd := exec.Command("ssh", "-Q", query)
output, err := cmd.Output()
if err != nil {
return []string{}, err
}
return strings.Split(string(output), "\n"), nil
}
func QueryOpenSSHOptions(
query string,
) ([]string, error) {
var availableQueries []string
key := query
if _cachedQueries[key] != nil && len(_cachedQueries[key]) > 0 {
return _cachedQueries[key], nil
} else {
availableQueries, err := queryValues(query)
if err != nil {
return []string{}, err
}
_cachedQueries[key] = availableQueries
}
return availableQueries, nil
}

View File

@ -4,52 +4,6 @@ import (
"config-lsp/common" "config-lsp/common"
) )
var BooleanEnumValue = common.EnumValue{
EnforceValues: true,
Values: []string{"yes", "no"},
}
var plusMinuxCaretPrefixes = []common.Prefix{
{
Prefix: "+",
Meaning: "Append to the default set",
},
{
Prefix: "-",
Meaning: "Remove from the default set",
},
{
Prefix: "^",
Meaning: "Place at the head of the default set",
},
}
func PrefixPlusMinusCaret(values []string) common.PrefixWithMeaningValue {
return common.PrefixWithMeaningValue{
Prefixes: []common.Prefix{
{
Prefix: "+",
Meaning: "Append to the default set",
},
{
Prefix: "-",
Meaning: "Remove from the default set",
},
{
Prefix: "^",
Meaning: "Place at the head of the default set",
},
},
SubValue: common.ArrayValue{
Separator: ",",
AllowDuplicates: false,
SubValue: common.EnumValue{
Values: values,
},
},
}
}
var Options = map[string]common.Option{ var Options = map[string]common.Option{
"AcceptEnv": common.NewOption( "AcceptEnv": common.NewOption(
`Specifies what environment variables sent by the client will be copied into the session's environ(7). See SendEnv and SetEnv in ssh_config(5) for how to configure the client. The TERM environment variable is always accepted whenever the client requests a pseudo-terminal as it is required by the protocol. Variables are specified by name, which may contain the wildcard characters * and ?. Multiple environment variables may be separated by whitespace or spread across multiple AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables.`, `Specifies what environment variables sent by the client will be copied into the session's environ(7). See SendEnv and SetEnv in ssh_config(5) for how to configure the client. The TERM environment variable is always accepted whenever the client requests a pseudo-terminal as it is required by the protocol. Variables are specified by name, which may contain the wildcard characters * and ?. Multiple environment variables may be separated by whitespace or spread across multiple AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables.`,
@ -73,7 +27,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
common.CustomValue{ common.CustomValue{
FetchValue: func() common.Value { FetchValue: func() common.Value {
return common.ArrayValue{ return common.ArrayValue{
AllowDuplicates: false, DuplicatesExtractor: &common.SimpleDuplicatesExtractor,
SubValue: common.StringValue{}, SubValue: common.StringValue{},
Separator: " ", Separator: " ",
} }
@ -113,7 +67,6 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
Values: []string{"any"}, Values: []string{"any"},
}, },
common.ArrayValue{ common.ArrayValue{
AllowDuplicates: true,
SubValue: common.EnumValue{ SubValue: common.EnumValue{
EnforceValues: true, EnforceValues: true,
Values: []string{ Values: []string{
@ -157,7 +110,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
common.ArrayValue{ common.ArrayValue{
SubValue: common.StringValue{}, SubValue: common.StringValue{},
Separator: " ", Separator: " ",
AllowDuplicates: false, DuplicatesExtractor: &common.DuplicatesAllowedExtractor,
}, },
), ),
"AuthorizedPrincipalsCommand": common.NewOption( "AuthorizedPrincipalsCommand": common.NewOption(
@ -200,7 +153,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
}, },
SubValue: common.ArrayValue{ SubValue: common.ArrayValue{
Separator: ",", Separator: ",",
AllowDuplicates: false, DuplicatesExtractor: &common.DuplicatesAllowedExtractor,
SubValue: common.StringValue{}, SubValue: common.StringValue{},
}, },
}, },
@ -222,7 +175,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
The default is not to expire channels of any type for inactivity.`, The default is not to expire channels of any type for inactivity.`,
common.ArrayValue{ common.ArrayValue{
Separator: " ", Separator: " ",
AllowDuplicates: false, DuplicatesExtractor: &ChannelTimeoutExtractor,
SubValue: common.KeyValueAssignmentValue{ SubValue: common.KeyValueAssignmentValue{
Separator: "=", Separator: "=",
Key: common.EnumValue{ Key: common.EnumValue{
@ -407,7 +360,6 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
Values: []string{"none"}, Values: []string{"none"},
}, },
common.ArrayValue{ common.ArrayValue{
AllowDuplicates: true,
Separator: " ", Separator: " ",
SubValue: common.EnumValue{ SubValue: common.EnumValue{
EnforceValues: true, EnforceValues: true,
@ -582,7 +534,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
common.ArrayValue{ common.ArrayValue{
SubValue: common.StringValue{}, SubValue: common.StringValue{},
Separator: ",", Separator: ",",
AllowDuplicates: false, DuplicatesExtractor: &common.DuplicatesAllowedExtractor,
}, },
}, },
}, },
@ -631,7 +583,6 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may
The verify-required option requires a FIDO key signature attest that the user was verified, e.g. via a PIN. The verify-required option requires a FIDO key signature attest that the user was verified, e.g. via a PIN.
Neither the touch-required or verify-required options have any effect for other, non-FIDO, public key types.`, Neither the touch-required or verify-required options have any effect for other, non-FIDO, public key types.`,
common.ArrayValue{ common.ArrayValue{
AllowDuplicates: true,
Separator: ",", Separator: ",",
SubValue: common.EnumValue{ SubValue: common.EnumValue{
EnforceValues: true, EnforceValues: true,