mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	helper/backend: start this thing
This commit is contained in:
		
							
								
								
									
										61
									
								
								helper/backend/backend.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								helper/backend/backend.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | package backend | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/hashicorp/vault/vault" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Backend is an implementation of vault.LogicalBackend that allows | ||||||
|  | // the implementer to code a backend using a much more programmer-friendly | ||||||
|  | // framework that handles a lot of the routing and validation for you. | ||||||
|  | // | ||||||
|  | // This is recommended over implementing vault.LogicalBackend directly. | ||||||
|  | type Backend struct { | ||||||
|  | 	Paths []*Path | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Path is a single path that the backend responds to. | ||||||
|  | type Path struct { | ||||||
|  | 	// Pattern is the pattern of the URL that matches this path. | ||||||
|  | 	// | ||||||
|  | 	// This should be a valid regular expression. Named captures will be | ||||||
|  | 	// exposed as fields that should map to a schema in Fields. If a named | ||||||
|  | 	// capture is not a field in the Fields map, then it will be ignored. | ||||||
|  | 	Pattern string | ||||||
|  |  | ||||||
|  | 	// Fields is the mapping of data fields to a schema describing that | ||||||
|  | 	// field. Named captures in the Pattern also map to fields. If a named | ||||||
|  | 	// capture name matches a PUT body name, the named capture takes | ||||||
|  | 	// priority. | ||||||
|  | 	// | ||||||
|  | 	// Note that only named capture fields are available in every operation, | ||||||
|  | 	// whereas all fields are avaiable in the Write operation. | ||||||
|  | 	Fields map[string]*FieldSchema | ||||||
|  |  | ||||||
|  | 	// Root if not blank, denotes that this path requires root | ||||||
|  | 	// privileges and the path pattern that is the root path. This can't | ||||||
|  | 	// be a regular expression and must be an exact path. It may have a | ||||||
|  | 	// trailing '*' to denote that it is a prefix, and not an exact match. | ||||||
|  | 	Root string | ||||||
|  |  | ||||||
|  | 	// Callback is what is called when this path is requested with | ||||||
|  | 	// a valid set of data. | ||||||
|  | 	Callback func(*vault.Request, *FieldData) (*vault.Response, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FieldSchema is a basic schema to describe the format of a path field. | ||||||
|  | type FieldSchema struct { | ||||||
|  | 	Type FieldType | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t FieldType) Zero() interface{} { | ||||||
|  | 	switch t { | ||||||
|  | 	case TypeString: | ||||||
|  | 		return "" | ||||||
|  | 	case TypeInt: | ||||||
|  | 		return 0 | ||||||
|  | 	case TypeBool: | ||||||
|  | 		return false | ||||||
|  | 	default: | ||||||
|  | 		panic("unknown type: " + t.String()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								helper/backend/field_data.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								helper/backend/field_data.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | package backend | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/mitchellh/mapstructure" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // FieldData is the structure passed to the callback to handle a path | ||||||
|  | // containing the populated parameters for fields. This should be used | ||||||
|  | // instead of the raw (*vault.Request).Data to access data in a type-safe | ||||||
|  | // way. | ||||||
|  | type FieldData struct { | ||||||
|  | 	Raw    map[string]interface{} | ||||||
|  | 	Schema map[string]*FieldSchema | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get gets the value for the given field. If the key is an invalid field, | ||||||
|  | // FieldData will panic. If you want a safer version of this method, use | ||||||
|  | // GetOk. If the field k is not set, the default value (if set) will be | ||||||
|  | // returned, otherwise the zero value will be returned. | ||||||
|  | func (d *FieldData) Get(k string) interface{} { | ||||||
|  | 	schema, ok := d.Schema[k] | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(fmt.Sprintf("field %s not in the schema", k)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	value, ok := d.GetOk(k) | ||||||
|  | 	if !ok { | ||||||
|  | 		value = schema.Type.Zero() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return value | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetOk gets the value for the given field. The second return value | ||||||
|  | // will be false if the key is invalid or the key is not set at all. | ||||||
|  | func (d *FieldData) GetOk(k string) (interface{}, bool) { | ||||||
|  | 	schema, ok := d.Schema[k] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	result, ok, err := d.GetOkErr(k) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(fmt.Sprintf("error reading %s: %s", k, err)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ok && result == nil { | ||||||
|  | 		result = schema.Type.Zero() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result, ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetOkErr is the most conservative of all the Get methods. It returns | ||||||
|  | // whether key is set or not, but also an error value. The error value is | ||||||
|  | // non-nil if the field doesn't exist or there was an error parsing the | ||||||
|  | // field value. | ||||||
|  | func (d *FieldData) GetOkErr(k string) (interface{}, bool, error) { | ||||||
|  | 	schema, ok := d.Schema[k] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, false, fmt.Errorf("unknown field: %s", k) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch schema.Type { | ||||||
|  | 	case TypeBool: | ||||||
|  | 		fallthrough | ||||||
|  | 	case TypeInt: | ||||||
|  | 		fallthrough | ||||||
|  | 	case TypeString: | ||||||
|  | 		return d.getPrimitive(k, schema) | ||||||
|  | 	default: | ||||||
|  | 		return nil, false, | ||||||
|  | 			fmt.Errorf("unknown field type %s for field %s", schema.Type, k) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *FieldData) getPrimitive( | ||||||
|  | 	k string, schema *FieldSchema) (interface{}, bool, error) { | ||||||
|  | 	raw, ok := d.Raw[k] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch schema.Type { | ||||||
|  | 	case TypeBool: | ||||||
|  | 		var result bool | ||||||
|  | 		if err := mapstructure.WeakDecode(raw, &result); err != nil { | ||||||
|  | 			return nil, true, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return result, true, nil | ||||||
|  | 	case TypeInt: | ||||||
|  | 		var result int | ||||||
|  | 		if err := mapstructure.WeakDecode(raw, &result); err != nil { | ||||||
|  | 			return nil, true, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return result, true, nil | ||||||
|  | 	case TypeString: | ||||||
|  | 		var result string | ||||||
|  | 		if err := mapstructure.WeakDecode(raw, &result); err != nil { | ||||||
|  | 			return nil, true, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return result, true, nil | ||||||
|  | 	default: | ||||||
|  | 		panic(fmt.Sprintf("Unknown type: %s", schema.Type)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								helper/backend/field_data_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								helper/backend/field_data_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | package backend | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestFieldDataGet(t *testing.T) { | ||||||
|  | 	cases := map[string]struct { | ||||||
|  | 		Schema map[string]*FieldSchema | ||||||
|  | 		Raw    map[string]interface{} | ||||||
|  | 		Key    string | ||||||
|  | 		Value  interface{} | ||||||
|  | 	}{ | ||||||
|  | 		"string type, string value": { | ||||||
|  | 			map[string]*FieldSchema{ | ||||||
|  | 				"foo": &FieldSchema{Type: TypeString}, | ||||||
|  | 			}, | ||||||
|  | 			map[string]interface{}{ | ||||||
|  | 				"foo": "bar", | ||||||
|  | 			}, | ||||||
|  | 			"foo", | ||||||
|  | 			"bar", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"string type, int value": { | ||||||
|  | 			map[string]*FieldSchema{ | ||||||
|  | 				"foo": &FieldSchema{Type: TypeString}, | ||||||
|  | 			}, | ||||||
|  | 			map[string]interface{}{ | ||||||
|  | 				"foo": 42, | ||||||
|  | 			}, | ||||||
|  | 			"foo", | ||||||
|  | 			"42", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"string type, unset value": { | ||||||
|  | 			map[string]*FieldSchema{ | ||||||
|  | 				"foo": &FieldSchema{Type: TypeString}, | ||||||
|  | 			}, | ||||||
|  | 			map[string]interface{}{}, | ||||||
|  | 			"foo", | ||||||
|  | 			"", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"int type, int value": { | ||||||
|  | 			map[string]*FieldSchema{ | ||||||
|  | 				"foo": &FieldSchema{Type: TypeInt}, | ||||||
|  | 			}, | ||||||
|  | 			map[string]interface{}{ | ||||||
|  | 				"foo": 42, | ||||||
|  | 			}, | ||||||
|  | 			"foo", | ||||||
|  | 			42, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		"bool type, bool value": { | ||||||
|  | 			map[string]*FieldSchema{ | ||||||
|  | 				"foo": &FieldSchema{Type: TypeBool}, | ||||||
|  | 			}, | ||||||
|  | 			map[string]interface{}{ | ||||||
|  | 				"foo": false, | ||||||
|  | 			}, | ||||||
|  | 			"foo", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for name, tc := range cases { | ||||||
|  | 		data := &FieldData{ | ||||||
|  | 			Raw:    tc.Raw, | ||||||
|  | 			Schema: tc.Schema, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		actual := data.Get(tc.Key) | ||||||
|  | 		if !reflect.DeepEqual(actual, tc.Value) { | ||||||
|  | 			t.Fatalf( | ||||||
|  | 				"bad: %s\n\nExpected: %#v\nGot: %#v", | ||||||
|  | 				name, tc.Value, actual) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								helper/backend/field_type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								helper/backend/field_type.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | package backend | ||||||
|  |  | ||||||
|  | //go:generate stringer -type=FieldType field_type.go | ||||||
|  |  | ||||||
|  | // FieldType is the enum of types that a field can be. | ||||||
|  | type FieldType uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	TypeInvalid FieldType = 0 | ||||||
|  | 	TypeString  FieldType = iota | ||||||
|  | 	TypeInt | ||||||
|  | 	TypeBool | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // FieldType has more methods defined on it in backend.go. They aren't | ||||||
|  | // in this file since stringer doesn't like that. | ||||||
							
								
								
									
										16
									
								
								helper/backend/fieldtype_string.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								helper/backend/fieldtype_string.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // generated by stringer -type=FieldType field_type.go; DO NOT EDIT | ||||||
|  |  | ||||||
|  | package backend | ||||||
|  |  | ||||||
|  | import "fmt" | ||||||
|  |  | ||||||
|  | const _FieldType_name = "TypeInvalidTypeStringTypeIntTypeBool" | ||||||
|  |  | ||||||
|  | var _FieldType_index = [...]uint8{0, 11, 21, 28, 36} | ||||||
|  |  | ||||||
|  | func (i FieldType) String() string { | ||||||
|  | 	if i+1 >= FieldType(len(_FieldType_index)) { | ||||||
|  | 		return fmt.Sprintf("FieldType(%d)", i) | ||||||
|  | 	} | ||||||
|  | 	return _FieldType_name[_FieldType_index[i]:_FieldType_index[i+1]] | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Mitchell Hashimoto
					Mitchell Hashimoto