This stores worker connection nonces within the database to ensure uniqueness of connections. Prior the connection required any attacker to be able to decrypt the incoming encrypted authentication message in order to replay (and if the attacker has access to the KMS you have bigger problems), but with this change even if an attacker does have access to the KMS key, they cannot reuse the same message. (If they're able to encrypt against the KMS key, they can still act as a worker with a new message, so this only really helps if they only have decryption capabilities.)
200 lines
5.3 KiB
Go
200 lines
5.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/golang/protobuf/ptypes/timestamp"
|
|
"github.com/golang/protobuf/ptypes/wrappers"
|
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
|
"google.golang.org/genproto/protobuf/field_mask"
|
|
)
|
|
|
|
type CustomValidatorFunc func() map[string]string
|
|
|
|
// A boundary resource identifier
|
|
type Id string
|
|
|
|
var NoopValidatorFn CustomValidatorFunc = func() map[string]string { return nil }
|
|
|
|
type ApiResource interface {
|
|
GetId() string
|
|
GetScope() *scopes.ScopeInfo
|
|
GetName() *wrappers.StringValue
|
|
GetDescription() *wrappers.StringValue
|
|
GetCreatedTime() *timestamp.Timestamp
|
|
GetUpdatedTime() *timestamp.Timestamp
|
|
GetVersion() uint32
|
|
}
|
|
|
|
func ValidateCreateRequest(i ApiResource, fn CustomValidatorFunc) error {
|
|
badFields := map[string]string{}
|
|
if i.GetId() != "" {
|
|
badFields["id"] = "This is a read only field."
|
|
}
|
|
if i.GetName() != nil {
|
|
trimmed := strings.TrimSpace(i.GetName().GetValue())
|
|
switch {
|
|
case trimmed == "":
|
|
badFields["name"] = "Cannot set empty string as name"
|
|
case !ValidNameDescription(trimmed):
|
|
badFields["name"] = "Name contains unprintable characters"
|
|
default:
|
|
i.GetName().Value = trimmed
|
|
}
|
|
}
|
|
if i.GetDescription() != nil {
|
|
trimmed := strings.TrimSpace(i.GetDescription().GetValue())
|
|
switch {
|
|
case trimmed == "":
|
|
badFields["description"] = "Cannot set empty string as description"
|
|
case !ValidNameDescription(trimmed):
|
|
badFields["description"] = "Description contains unprintable characters"
|
|
default:
|
|
i.GetDescription().Value = trimmed
|
|
}
|
|
}
|
|
if i.GetCreatedTime() != nil {
|
|
badFields["created_time"] = "This is a read only field."
|
|
}
|
|
if i.GetUpdatedTime() != nil {
|
|
badFields["updated_time"] = "This is a read only field."
|
|
}
|
|
if i.GetVersion() != 0 {
|
|
badFields["version"] = "Cannot specify this field in a create request."
|
|
}
|
|
for k, v := range fn() {
|
|
badFields[k] = v
|
|
}
|
|
if len(badFields) > 0 {
|
|
return InvalidArgumentErrorf("Error in provided request.", badFields)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type UpdateRequest interface {
|
|
GetId() string
|
|
GetUpdateMask() *field_mask.FieldMask
|
|
}
|
|
|
|
func ValidateUpdateRequest(r UpdateRequest, i ApiResource, fn CustomValidatorFunc, prefix ...string) error {
|
|
badFields := map[string]string{}
|
|
if !ValidId(Id(r.GetId()), prefix...) {
|
|
badFields["id"] = "Improperly formatted path identifier."
|
|
}
|
|
if r.GetUpdateMask() == nil {
|
|
badFields["update_mask"] = "UpdateMask not provided but is required to update this resource."
|
|
}
|
|
|
|
if i == nil {
|
|
// It is legitimate for no item to be specified in an update request as it indicates all fields provided in
|
|
// the mask will be marked as unset.
|
|
return nil
|
|
}
|
|
if i.GetName() != nil {
|
|
trimmed := strings.TrimSpace(i.GetName().GetValue())
|
|
switch {
|
|
case trimmed == "":
|
|
badFields["name"] = "Cannot set empty string as name"
|
|
case !ValidNameDescription(trimmed):
|
|
badFields["name"] = "Name contains unprintable characters"
|
|
default:
|
|
i.GetName().Value = trimmed
|
|
}
|
|
}
|
|
if i.GetDescription() != nil {
|
|
trimmed := strings.TrimSpace(i.GetDescription().GetValue())
|
|
switch {
|
|
case trimmed == "":
|
|
badFields["description"] = "Cannot set empty string as description"
|
|
case !ValidNameDescription(trimmed):
|
|
badFields["description"] = "Description contains unprintable characters"
|
|
default:
|
|
i.GetDescription().Value = trimmed
|
|
}
|
|
}
|
|
if i.GetVersion() == 0 {
|
|
badFields["version"] = "Existing resource version is required for an update."
|
|
}
|
|
if i.GetId() != "" {
|
|
badFields["id"] = "This is a read only field and cannot be specified in an update request."
|
|
}
|
|
if i.GetCreatedTime() != nil {
|
|
badFields["created_time"] = "This is a read only field and cannot be specified in an update request."
|
|
}
|
|
if i.GetUpdatedTime() != nil {
|
|
badFields["updated_time"] = "This is a read only field and cannot be specified in an update request."
|
|
}
|
|
|
|
for k, v := range fn() {
|
|
badFields[k] = v
|
|
}
|
|
|
|
if len(badFields) > 0 {
|
|
return InvalidArgumentErrorf("Error in provided request.", badFields)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type GetRequest interface {
|
|
GetId() string
|
|
}
|
|
|
|
func ValidateGetRequest(fn CustomValidatorFunc, r GetRequest, prefix ...string) error {
|
|
badFields := map[string]string{}
|
|
if !ValidId(Id(r.GetId()), prefix...) {
|
|
badFields["id"] = "Invalid formatted identifier."
|
|
}
|
|
for k, v := range fn() {
|
|
badFields[k] = v
|
|
}
|
|
if len(badFields) > 0 {
|
|
return InvalidArgumentErrorf("Error in provided request.", badFields)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type DeleteRequest interface {
|
|
GetId() string
|
|
}
|
|
|
|
func ValidateDeleteRequest(fn CustomValidatorFunc, r DeleteRequest, prefix ...string) error {
|
|
badFields := map[string]string{}
|
|
if !ValidId(Id(r.GetId()), prefix...) {
|
|
badFields["id"] = "Incorrectly formatted identifier."
|
|
}
|
|
for k, v := range fn() {
|
|
badFields[k] = v
|
|
}
|
|
if len(badFields) > 0 {
|
|
return InvalidArgumentErrorf("Error in provided request.", badFields)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var reInvalidID = regexp.MustCompile("[^A-Za-z0-9]")
|
|
|
|
func ValidId(i Id, prefixes ...string) bool {
|
|
id := string(i)
|
|
for _, prefix := range prefixes {
|
|
prefix = prefix + "_"
|
|
if !strings.HasPrefix(id, prefix) {
|
|
continue
|
|
}
|
|
id = strings.TrimPrefix(id, prefix)
|
|
if !reInvalidID.MatchString(id) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func ValidNameDescription(in string) bool {
|
|
idx := strings.IndexFunc(in, func(c rune) bool {
|
|
return !unicode.IsPrint(c)
|
|
})
|
|
|
|
return idx == -1
|
|
}
|