[#908] adm/frostfsid: Add parameter validations

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2024-01-17 11:35:09 +03:00 committed by Evgenii Stratonikov
parent 136acdba21
commit e42262a863
3 changed files with 199 additions and 15 deletions

View file

@ -28,6 +28,8 @@ const (
groupIDFlag = "group-id" groupIDFlag = "group-id"
) )
const rootNamespacePlaceholder = "<root>"
var ( var (
frostfsidCmd = &cobra.Command{ frostfsidCmd = &cobra.Command{
Use: "frostfsid", Use: "frostfsid",
@ -220,7 +222,7 @@ func initFrostfsIDListGroupSubjectsCmd() {
} }
func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) { func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
commonCmd.ExitOnErr(cmd, "init contract client: %w", err) commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
@ -241,13 +243,16 @@ func frostfsidListNamespaces(cmd *cobra.Command, _ []string) {
sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name }) sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name })
for _, namespace := range namespaces { for _, namespace := range namespaces {
cmd.Printf("%q\n", namespace.Name) if namespace.Name == "" {
namespace.Name = rootNamespacePlaceholder
}
cmd.Printf("%s\n", namespace.Name)
} }
} }
func frostfsidCreateSubject(cmd *cobra.Command, _ []string) { func frostfsidCreateSubject(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
subjName, _ := cmd.Flags().GetString(subjectNameFlag) subjName := getFrostfsIDSubjectName(cmd)
subjKey := getFrostfsIDSubjectKey(cmd) subjKey := getFrostfsIDSubjectKey(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
@ -275,7 +280,7 @@ func frostfsidDeleteSubject(cmd *cobra.Command, _ []string) {
} }
func frostfsidListSubjects(cmd *cobra.Command, _ []string) { func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag) includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
@ -300,8 +305,8 @@ func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
} }
func frostfsidCreateGroup(cmd *cobra.Command, _ []string) { func frostfsidCreateGroup(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
groupName, _ := cmd.Flags().GetString(groupNameFlag) groupName := getFrostfsIDGroupName(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
commonCmd.ExitOnErr(cmd, "init contract client: %w", err) commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
@ -315,8 +320,8 @@ func frostfsidCreateGroup(cmd *cobra.Command, _ []string) {
} }
func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) { func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
groupID, _ := cmd.Flags().GetInt64(groupIDFlag) groupID := getFrostfsIDGroupID(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
commonCmd.ExitOnErr(cmd, "init contract client: %w", err) commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
@ -328,7 +333,7 @@ func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) {
} }
func frostfsidListGroups(cmd *cobra.Command, _ []string) { func frostfsidListGroups(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err) commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
@ -339,13 +344,13 @@ func frostfsidListGroups(cmd *cobra.Command, _ []string) {
sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name }) sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
for _, group := range groups { for _, group := range groups {
cmd.Printf("%q (%d)\n", group.Name, group.ID) cmd.Printf("%s (%d)\n", group.Name, group.ID)
} }
} }
func frostfsidAddSubjectToGroup(cmd *cobra.Command, _ []string) { func frostfsidAddSubjectToGroup(cmd *cobra.Command, _ []string) {
subjectAddress := getFrostfsIDSubjectAddress(cmd) subjectAddress := getFrostfsIDSubjectAddress(cmd)
groupID, _ := cmd.Flags().GetInt64(groupIDFlag) groupID := getFrostfsIDGroupID(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
commonCmd.ExitOnErr(cmd, "init contract client: %w", err) commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
@ -358,7 +363,7 @@ func frostfsidAddSubjectToGroup(cmd *cobra.Command, _ []string) {
func frostfsidRemoveSubjectFromGroup(cmd *cobra.Command, _ []string) { func frostfsidRemoveSubjectFromGroup(cmd *cobra.Command, _ []string) {
subjectAddress := getFrostfsIDSubjectAddress(cmd) subjectAddress := getFrostfsIDSubjectAddress(cmd)
groupID, _ := cmd.Flags().GetInt64(groupIDFlag) groupID := getFrostfsIDGroupID(cmd)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)
commonCmd.ExitOnErr(cmd, "init contract client: %w", err) commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
@ -370,8 +375,8 @@ func frostfsidRemoveSubjectFromGroup(cmd *cobra.Command, _ []string) {
} }
func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) { func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
ns, _ := cmd.Flags().GetString(namespaceFlag) ns := getFrostfsIDNamespace(cmd)
groupID, _ := cmd.Flags().GetInt64(groupIDFlag) groupID := getFrostfsIDGroupID(cmd)
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag) includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
ffsid, err := newFrostfsIDClient(cmd) ffsid, err := newFrostfsIDClient(cmd)

View file

@ -1,7 +1,9 @@
package morph package morph
import ( import (
"errors"
"fmt" "fmt"
"regexp"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -11,6 +13,14 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var (
frostfsidSubjectNameRegexp = regexp.MustCompile(`^[\w+=,.@-]{1,64}$`)
frostfsidGroupNameRegexp = regexp.MustCompile(`^[\w+=,.@-]{1,128}$`)
// frostfsidNamespaceNameRegexp similar to https://git.frostfs.info/TrueCloudLab/frostfs-contract/src/commit/f2a82aa635aa57d9b05092d8cf15b170b53cc324/nns/nns_contract.go#L690
frostfsidNamespaceNameRegexp = regexp.MustCompile(`(^$)|(^[a-z0-9]{1,2}$)|(^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$)`)
)
func getFrostfsIDAdmin(v *viper.Viper) (util.Uint160, bool, error) { func getFrostfsIDAdmin(v *viper.Viper) (util.Uint160, bool, error) {
admin := v.GetString(frostfsIDAdminConfigKey) admin := v.GetString(frostfsIDAdminConfigKey)
if admin == "" { if admin == "" {
@ -47,3 +57,53 @@ func getFrostfsIDSubjectAddress(cmd *cobra.Command) util.Uint160 {
commonCmd.ExitOnErr(cmd, "invalid subject address: %w", err) commonCmd.ExitOnErr(cmd, "invalid subject address: %w", err)
return subjAddr return subjAddr
} }
func getFrostfsIDSubjectName(cmd *cobra.Command) string {
subjectName, _ := cmd.Flags().GetString(subjectNameFlag)
if subjectName == "" {
return ""
}
if !frostfsidSubjectNameRegexp.MatchString(subjectName) {
commonCmd.ExitOnErr(cmd, "invalid subject name: %w",
fmt.Errorf("name must match regexp: %s", frostfsidSubjectNameRegexp.String()))
}
return subjectName
}
func getFrostfsIDGroupName(cmd *cobra.Command) string {
groupName, _ := cmd.Flags().GetString(groupNameFlag)
if !frostfsidGroupNameRegexp.MatchString(groupName) {
commonCmd.ExitOnErr(cmd, "invalid group name: %w",
fmt.Errorf("name must match regexp: %s", frostfsidGroupNameRegexp.String()))
}
return groupName
}
func getFrostfsIDGroupID(cmd *cobra.Command) int64 {
groupID, _ := cmd.Flags().GetInt64(groupIDFlag)
if groupID <= 0 {
commonCmd.ExitOnErr(cmd, "invalid group id: %w",
errors.New("group id must be positive integer"))
}
return groupID
}
func getFrostfsIDNamespace(cmd *cobra.Command) string {
ns, _ := cmd.Flags().GetString(namespaceFlag)
if ns == rootNamespacePlaceholder {
ns = ""
}
if !frostfsidNamespaceNameRegexp.MatchString(ns) {
commonCmd.ExitOnErr(cmd, "invalid namespace: %w",
fmt.Errorf("name must match regexp: %s", frostfsidNamespaceNameRegexp.String()))
}
return ns
}

View file

@ -51,3 +51,122 @@ func TestFrostfsIDConfig(t *testing.T) {
require.False(t, found) require.False(t, found)
}) })
} }
func TestNamespaceRegexp(t *testing.T) {
for _, tc := range []struct {
name string
namespace string
matched bool
}{
{
name: "root empty ns",
namespace: "",
matched: true,
},
{
name: "simple valid ns",
namespace: "my-namespace-123",
matched: true,
},
{
name: "root placeholder",
namespace: "<root>",
matched: false,
},
{
name: "too long",
namespace: "abcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyz",
matched: false,
},
{
name: "start with hyphen",
namespace: "-ns",
matched: false,
},
{
name: "end with hyphen",
namespace: "ns-",
matched: false,
},
{
name: "with spaces",
namespace: "ns ns",
matched: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.matched, frostfsidNamespaceNameRegexp.MatchString(tc.namespace))
})
}
}
func TestSubjectNameRegexp(t *testing.T) {
for _, tc := range []struct {
name string
subject string
matched bool
}{
{
name: "empty",
subject: "",
matched: false,
},
{
name: "invalid",
subject: "invalid{name}",
matched: false,
},
{
name: "too long",
subject: "abcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyz",
matched: false,
},
{
name: "valid",
subject: "valid_name.012345@6789",
matched: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.matched, frostfsidSubjectNameRegexp.MatchString(tc.subject))
})
}
}
func TestSubjectGroupRegexp(t *testing.T) {
for _, tc := range []struct {
name string
subject string
matched bool
}{
{
name: "empty",
subject: "",
matched: false,
},
{
name: "invalid",
subject: "invalid{name}",
matched: false,
},
{
name: "too long",
subject: "abcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyz",
matched: false,
},
{
name: "long",
subject: "abcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyzabcdefghijklmnopkrstuvwxyz",
matched: true,
},
{
name: "valid",
subject: "valid_name.012345@6789",
matched: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.matched, frostfsidGroupNameRegexp.MatchString(tc.subject))
})
}
}