<commit_before>package sarama

import (
	"encoding/binary"
	"math"
)

var errInvalidArrayLength = PacketDecodingError{"invalid array length"}
var errInvalidByteSliceLength = PacketDecodingError{"invalid byteslice length"}
var errInvalidByteSliceLengthType = PacketDecodingError{"invalid byteslice length type"}
var errInvalidStringLength = PacketDecodingError{"invalid string length"}
var errInvalidSubsetSize = PacketDecodingError{"invalid subset size"}
var errVarintOverflow = PacketDecodingError{"varint overflow"}

type realDecoder struct {
	raw   []byte
	off   int
	stack []pushDecoder
}

// primitives

func (rd *realDecoder) getInt8() (int8, error) {
	if rd.remaining() < 1 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int8(rd.raw[rd.off])
	rd.off++
	return tmp, nil
}

func (rd *realDecoder) getInt16() (int16, error) {
	if rd.remaining() < 2 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int16(binary.BigEndian.Uint16(rd.raw[rd.off:]))
	rd.off += 2
	return tmp, nil
}

func (rd *realDecoder) getInt32() (int32, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4
	return tmp, nil
}

func (rd *realDecoder) getInt64() (int64, error) {
	if rd.remaining() < 8 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int64(binary.BigEndian.Uint64(rd.raw[rd.off:]))
	rd.off += 8
	return tmp, nil
}

func (rd *realDecoder) getVarint() (int64, error) {
	tmp, n := binary.Varint(rd.raw[rd.off:])
	if n == 0 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	if n < 0 {
		rd.off -= n
		return -1, errVarintOverflow
	}
	rd.off += n
	return tmp, nil
}

func (rd *realDecoder) getArrayLength() (int, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4
	if tmp > rd.remaining() {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	} else if tmp > 2*math.MaxUint16 {
		return -1, errInvalidArrayLength
	}
	return tmp, nil
}

// collections

func (rd *realDecoder) getBytes() ([]byte, error) {
	tmp, err := rd.getInt32()
	if err != nil {
		return nil, err
	}
	if tmp == -1 {
		return nil, nil
	}

	return rd.getRawBytes(int(tmp))
}

func (rd *realDecoder) getVarintBytes() ([]byte, error) {
	tmp, err := rd.getVarint()
	if err != nil {
		return nil, err
	}
	if tmp == -1 {
		return nil, nil
	}

	return rd.getRawBytes(int(tmp))
}

func (rd *realDecoder) getString() (string, error) {
	tmp, err := rd.getInt16()

	if err != nil {
		return "", err
	}

	n := int(tmp)

	switch {
	case n < -1:
		return "", errInvalidStringLength
	case n == -1:
		return "", nil
	case n == 0:
		return "", nil
	case n > rd.remaining():
		rd.off = len(rd.raw)
		return "", ErrInsufficientData
	}

	tmpStr := string(rd.raw[rd.off : rd.off+n])
	rd.off += n
	return tmpStr, nil
}

func (rd *realDecoder) getInt32Array() ([]int32, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}
	n := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4

	if rd.remaining() < 4*n {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}

	if n == 0 {
		return nil, nil
	}

	if n < 0 {
		return nil, errInvalidArrayLength
	}

	ret := make([]int32, n)
	for i := range ret {
		ret[i] = int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))
		rd.off += 4
	}
	return ret, nil
}

func (rd *realDecoder) getInt64Array() ([]int64, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}
	n := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4

	if rd.remaining() < 8*n {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}

	if n == 0 {
		return nil, nil
	}

	if n < 0 {
		return nil, errInvalidArrayLength
	}

	ret := make([]int64, n)
	for i := range ret {
		ret[i] = int64(binary.BigEndian.Uint64(rd.raw[rd.off:]))
		rd.off += 8
	}
	return ret, nil
}

func (rd *realDecoder) getStringArray() ([]string, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}
	n := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4

	if n == 0 {
		return nil, nil
	}

	if n < 0 {
		return nil, errInvalidArrayLength
	}

	ret := make([]string, n)
	for i := range ret {
		str, err := rd.getString()
		if err != nil {
			return nil, err
		}

		ret[i] = str
	}
	return ret, nil
}

// subsets

func (rd *realDecoder) remaining() int {
	return len(rd.raw) - rd.off
}

func (rd *realDecoder) getSubset(length int) (packetDecoder, error) {
	buf, err := rd.getRawBytes(length)
	if err != nil {
		return nil, err
	}
	return &realDecoder{raw: buf}, nil
}

func (rd *realDecoder) getRawBytes(length int) ([]byte, error) {
	if length < 0 {
		return nil, errInvalidByteSliceLength
	} else if length > rd.remaining() {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}

	start := rd.off
	rd.off += length
	return rd.raw[start:rd.off], nil
}

// stacks

func (rd *realDecoder) push(in pushDecoder) error {
	in.saveOffset(rd.off)

	reserve := in.reserveLength()
	if rd.remaining() < reserve {
		rd.off = len(rd.raw)
		return ErrInsufficientData
	}

	rd.stack = append(rd.stack, in)

	rd.off += reserve

	return nil
}

func (rd *realDecoder) pop() error {
	// this is go's ugly pop pattern (the inverse of append)
	in := rd.stack[len(rd.stack)-1]
	rd.stack = rd.stack[:len(rd.stack)-1]

	return in.check(rd.off, rd.raw)
}
<commit_msg>Allow negative values for getArrayLength()<commit_after>package sarama

import (
	"encoding/binary"
	"math"
)

var errInvalidArrayLength = PacketDecodingError{"invalid array length"}
var errInvalidByteSliceLength = PacketDecodingError{"invalid byteslice length"}
var errInvalidByteSliceLengthType = PacketDecodingError{"invalid byteslice length type"}
var errInvalidStringLength = PacketDecodingError{"invalid string length"}
var errInvalidSubsetSize = PacketDecodingError{"invalid subset size"}
var errVarintOverflow = PacketDecodingError{"varint overflow"}

type realDecoder struct {
	raw   []byte
	off   int
	stack []pushDecoder
}

// primitives

func (rd *realDecoder) getInt8() (int8, error) {
	if rd.remaining() < 1 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int8(rd.raw[rd.off])
	rd.off++
	return tmp, nil
}

func (rd *realDecoder) getInt16() (int16, error) {
	if rd.remaining() < 2 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int16(binary.BigEndian.Uint16(rd.raw[rd.off:]))
	rd.off += 2
	return tmp, nil
}

func (rd *realDecoder) getInt32() (int32, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4
	return tmp, nil
}

func (rd *realDecoder) getInt64() (int64, error) {
	if rd.remaining() < 8 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int64(binary.BigEndian.Uint64(rd.raw[rd.off:]))
	rd.off += 8
	return tmp, nil
}

func (rd *realDecoder) getVarint() (int64, error) {
	tmp, n := binary.Varint(rd.raw[rd.off:])
	if n == 0 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	if n < 0 {
		rd.off -= n
		return -1, errVarintOverflow
	}
	rd.off += n
	return tmp, nil
}

func (rd *realDecoder) getArrayLength() (int, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	}
	tmp := int(int32(binary.BigEndian.Uint32(rd.raw[rd.off:])))
	rd.off += 4
	if tmp > rd.remaining() {
		rd.off = len(rd.raw)
		return -1, ErrInsufficientData
	} else if tmp > 2*math.MaxUint16 {
		return -1, errInvalidArrayLength
	}
	return tmp, nil
}

// collections

func (rd *realDecoder) getBytes() ([]byte, error) {
	tmp, err := rd.getInt32()
	if err != nil {
		return nil, err
	}
	if tmp == -1 {
		return nil, nil
	}

	return rd.getRawBytes(int(tmp))
}

func (rd *realDecoder) getVarintBytes() ([]byte, error) {
	tmp, err := rd.getVarint()
	if err != nil {
		return nil, err
	}
	if tmp == -1 {
		return nil, nil
	}

	return rd.getRawBytes(int(tmp))
}

func (rd *realDecoder) getString() (string, error) {
	tmp, err := rd.getInt16()

	if err != nil {
		return "", err
	}

	n := int(tmp)

	switch {
	case n < -1:
		return "", errInvalidStringLength
	case n == -1:
		return "", nil
	case n == 0:
		return "", nil
	case n > rd.remaining():
		rd.off = len(rd.raw)
		return "", ErrInsufficientData
	}

	tmpStr := string(rd.raw[rd.off : rd.off+n])
	rd.off += n
	return tmpStr, nil
}

func (rd *realDecoder) getInt32Array() ([]int32, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}
	n := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4

	if rd.remaining() < 4*n {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}

	if n == 0 {
		return nil, nil
	}

	if n < 0 {
		return nil, errInvalidArrayLength
	}

	ret := make([]int32, n)
	for i := range ret {
		ret[i] = int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))
		rd.off += 4
	}
	return ret, nil
}

func (rd *realDecoder) getInt64Array() ([]int64, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}
	n := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4

	if rd.remaining() < 8*n {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}

	if n == 0 {
		return nil, nil
	}

	if n < 0 {
		return nil, errInvalidArrayLength
	}

	ret := make([]int64, n)
	for i := range ret {
		ret[i] = int64(binary.BigEndian.Uint64(rd.raw[rd.off:]))
		rd.off += 8
	}
	return ret, nil
}

func (rd *realDecoder) getStringArray() ([]string, error) {
	if rd.remaining() < 4 {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}
	n := int(binary.BigEndian.Uint32(rd.raw[rd.off:]))
	rd.off += 4

	if n == 0 {
		return nil, nil
	}

	if n < 0 {
		return nil, errInvalidArrayLength
	}

	ret := make([]string, n)
	for i := range ret {
		str, err := rd.getString()
		if err != nil {
			return nil, err
		}

		ret[i] = str
	}
	return ret, nil
}

// subsets

func (rd *realDecoder) remaining() int {
	return len(rd.raw) - rd.off
}

func (rd *realDecoder) getSubset(length int) (packetDecoder, error) {
	buf, err := rd.getRawBytes(length)
	if err != nil {
		return nil, err
	}
	return &realDecoder{raw: buf}, nil
}

func (rd *realDecoder) getRawBytes(length int) ([]byte, error) {
	if length < 0 {
		return nil, errInvalidByteSliceLength
	} else if length > rd.remaining() {
		rd.off = len(rd.raw)
		return nil, ErrInsufficientData
	}

	start := rd.off
	rd.off += length
	return rd.raw[start:rd.off], nil
}

// stacks

func (rd *realDecoder) push(in pushDecoder) error {
	in.saveOffset(rd.off)

	reserve := in.reserveLength()
	if rd.remaining() < reserve {
		rd.off = len(rd.raw)
		return ErrInsufficientData
	}

	rd.stack = append(rd.stack, in)

	rd.off += reserve

	return nil
}

func (rd *realDecoder) pop() error {
	// this is go's ugly pop pattern (the inverse of append)
	in := rd.stack[len(rd.stack)-1]
	rd.stack = rd.stack[:len(rd.stack)-1]

	return in.check(rd.off, rd.raw)
}
<|endoftext|>


<commit_before>package schema

import (
	"fmt"
	"strconv"
	"strings"
	"sync"

	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/mapstructure"
)

// ConfigFieldReader reads fields out of an untyped map[string]string to the
// best of its ability. It also applies defaults from the Schema. (The other
// field readers do not need default handling because they source fully
// populated data structures.)
type ConfigFieldReader struct {
	Config *terraform.ResourceConfig
	Schema map[string]*Schema

	indexMaps map[string]map[string]int
	once      sync.Once
}

func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) {
	r.once.Do(func() { r.indexMaps = make(map[string]map[string]int) })
	return r.readField(address, false)
}

func (r *ConfigFieldReader) readField(
	address []string, nested bool) (FieldReadResult, error) {
	schemaList := addrToSchema(address, r.Schema)
	if len(schemaList) == 0 {
		return FieldReadResult{}, nil
	}

	if !nested {
		// If we have a set anywhere in the address, then we need to
		// read that set out in order and actually replace that part of
		// the address with the real list index. i.e. set.50 might actually
		// map to set.12 in the config, since it is in list order in the
		// config, not indexed by set value.
		for i, v := range schemaList {
			// Sets are the only thing that cause this issue.
			if v.Type != TypeSet {
				continue
			}

			// If we're at the end of the list, then we don't have to worry
			// about this because we're just requesting the whole set.
			if i == len(schemaList)-1 {
				continue
			}

			// If we're looking for the count, then ignore...
			if address[i+1] == "#" {
				continue
			}

			indexMap, ok := r.indexMaps[strings.Join(address[:i+1], ".")]
			if !ok {
				// Get the set so we can get the index map that tells us the
				// mapping of the hash code to the list index
				_, err := r.readSet(address[:i+1], v)
				if err != nil {
					return FieldReadResult{}, err
				}
				indexMap = r.indexMaps[strings.Join(address[:i+1], ".")]
			}

			index, ok := indexMap[address[i+1]]
			if !ok {
				return FieldReadResult{}, nil
			}

			address[i+1] = strconv.FormatInt(int64(index), 10)
		}
	}

	k := strings.Join(address, ".")
	schema := schemaList[len(schemaList)-1]
	switch schema.Type {
	case TypeBool, TypeFloat, TypeInt, TypeString:
		return r.readPrimitive(k, schema)
	case TypeList:
		return readListField(&nestedConfigFieldReader{r}, address, schema)
	case TypeMap:
		return r.readMap(k)
	case TypeSet:
		return r.readSet(address, schema)
	case typeObject:
		return readObjectField(
			&nestedConfigFieldReader{r},
			address, schema.Elem.(map[string]*Schema))
	default:
		panic(fmt.Sprintf("Unknown type: %s", schema.Type))
	}
}

func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) {
	// We want both the raw value and the interpolated. We use the interpolated
	// to store actual values and we use the raw one to check for
	// computed keys. Actual values are obtained in the switch, depending on
	// the type of the raw value.
	mraw, ok := r.Config.GetRaw(k)
	if !ok {
		return FieldReadResult{}, nil
	}

	result := make(map[string]interface{})
	computed := false
	switch m := mraw.(type) {
	case string:
		// This is a map which has come out of an interpolated variable, so we
		// can just get the value directly from config. Values cannot be computed
		// currently.
		v, _ := r.Config.Get(k)

		// If this isn't a map[string]interface, it must be computed.
		mapV, ok := v.(map[string]interface{})
		if !ok {
			return FieldReadResult{
				Exists:   true,
				Computed: true,
			}, nil
		}

		// Otherwise we can proceed as usual.
		for i, iv := range mapV {
			result[i] = iv
		}
	case []interface{}:
		for i, innerRaw := range m {
			for ik := range innerRaw.(map[string]interface{}) {
				key := fmt.Sprintf("%s.%d.%s", k, i, ik)
				if r.Config.IsComputed(key) {
					computed = true
					break
				}

				v, _ := r.Config.Get(key)
				result[ik] = v
			}
		}
	case []map[string]interface{}:
		for i, innerRaw := range m {
			for ik := range innerRaw {
				key := fmt.Sprintf("%s.%d.%s", k, i, ik)
				if r.Config.IsComputed(key) {
					computed = true
					break
				}

				v, _ := r.Config.Get(key)
				result[ik] = v
			}
		}
	case map[string]interface{}:
		for ik := range m {
			key := fmt.Sprintf("%s.%s", k, ik)
			if r.Config.IsComputed(key) {
				computed = true
				break
			}

			v, _ := r.Config.Get(key)
			result[ik] = v
		}
	default:
		panic(fmt.Sprintf("unknown type: %#v", mraw))
	}

	var value interface{}
	if !computed {
		value = result
	}

	return FieldReadResult{
		Value:    value,
		Exists:   true,
		Computed: computed,
	}, nil
}

func (r *ConfigFieldReader) readPrimitive(
	k string, schema *Schema) (FieldReadResult, error) {
	raw, ok := r.Config.Get(k)
	if !ok {
		// Nothing in config, but we might still have a default from the schema
		var err error
		raw, err = schema.DefaultValue()
		if err != nil {
			return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err)
		}

		if raw == nil {
			return FieldReadResult{}, nil
		}
	}

	var result string
	if err := mapstructure.WeakDecode(raw, &result); err != nil {
		return FieldReadResult{}, err
	}

	computed := r.Config.IsComputed(k)
	returnVal, err := stringToPrimitive(result, computed, schema)
	if err != nil {
		return FieldReadResult{}, err
	}

	return FieldReadResult{
		Value:    returnVal,
		Exists:   true,
		Computed: computed,
	}, nil
}

func (r *ConfigFieldReader) readSet(
	address []string, schema *Schema) (FieldReadResult, error) {
	indexMap := make(map[string]int)
	// Create the set that will be our result
	set := schema.ZeroValue().(*Set)

	raw, err := readListField(&nestedConfigFieldReader{r}, address, schema)
	if err != nil {
		return FieldReadResult{}, err
	}
	if !raw.Exists {
		return FieldReadResult{Value: set}, nil
	}

	// If the list is computed, the set is necessarilly computed
	if raw.Computed {
		return FieldReadResult{
			Value:    set,
			Exists:   true,
			Computed: raw.Computed,
		}, nil
	}

	// Build up the set from the list elements
	for i, v := range raw.Value.([]interface{}) {
		// Check if any of the keys in this item are computed
		computed := r.hasComputedSubKeys(
			fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema)

		code := set.add(v, computed)
		indexMap[code] = i
	}

	r.indexMaps[strings.Join(address, ".")] = indexMap

	return FieldReadResult{
		Value:  set,
		Exists: true,
	}, nil
}

// hasComputedSubKeys walks through a schema and returns whether or not the
// given key contains any subkeys that are computed.
func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool {
	prefix := key + "."

	switch t := schema.Elem.(type) {
	case *Resource:
		for k, schema := range t.Schema {
			if r.Config.IsComputed(prefix + k) {
				return true
			}

			if r.hasComputedSubKeys(prefix+k, schema) {
				return true
			}
		}
	}

	return false
}

// nestedConfigFieldReader is a funny little thing that just wraps a
// ConfigFieldReader to call readField when ReadField is called so that
// we don't recalculate the set rewrites in the address, which leads to
// an infinite loop.
type nestedConfigFieldReader struct {
	Reader *ConfigFieldReader
}

func (r *nestedConfigFieldReader) ReadField(
	address []string) (FieldReadResult, error) {
	return r.Reader.readField(address, true)
}
<commit_msg>core: Ensure hasComputedSubKeys iterates over Sets and Lists properly<commit_after>package schema

import (
	"fmt"
	"strconv"
	"strings"
	"sync"

	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/mapstructure"
)

// ConfigFieldReader reads fields out of an untyped map[string]string to the
// best of its ability. It also applies defaults from the Schema. (The other
// field readers do not need default handling because they source fully
// populated data structures.)
type ConfigFieldReader struct {
	Config *terraform.ResourceConfig
	Schema map[string]*Schema

	indexMaps map[string]map[string]int
	once      sync.Once
}

func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) {
	r.once.Do(func() { r.indexMaps = make(map[string]map[string]int) })
	return r.readField(address, false)
}

func (r *ConfigFieldReader) readField(
	address []string, nested bool) (FieldReadResult, error) {
	schemaList := addrToSchema(address, r.Schema)
	if len(schemaList) == 0 {
		return FieldReadResult{}, nil
	}

	if !nested {
		// If we have a set anywhere in the address, then we need to
		// read that set out in order and actually replace that part of
		// the address with the real list index. i.e. set.50 might actually
		// map to set.12 in the config, since it is in list order in the
		// config, not indexed by set value.
		for i, v := range schemaList {
			// Sets are the only thing that cause this issue.
			if v.Type != TypeSet {
				continue
			}

			// If we're at the end of the list, then we don't have to worry
			// about this because we're just requesting the whole set.
			if i == len(schemaList)-1 {
				continue
			}

			// If we're looking for the count, then ignore...
			if address[i+1] == "#" {
				continue
			}

			indexMap, ok := r.indexMaps[strings.Join(address[:i+1], ".")]
			if !ok {
				// Get the set so we can get the index map that tells us the
				// mapping of the hash code to the list index
				_, err := r.readSet(address[:i+1], v)
				if err != nil {
					return FieldReadResult{}, err
				}
				indexMap = r.indexMaps[strings.Join(address[:i+1], ".")]
			}

			index, ok := indexMap[address[i+1]]
			if !ok {
				return FieldReadResult{}, nil
			}

			address[i+1] = strconv.FormatInt(int64(index), 10)
		}
	}

	k := strings.Join(address, ".")
	schema := schemaList[len(schemaList)-1]
	switch schema.Type {
	case TypeBool, TypeFloat, TypeInt, TypeString:
		return r.readPrimitive(k, schema)
	case TypeList:
		return readListField(&nestedConfigFieldReader{r}, address, schema)
	case TypeMap:
		return r.readMap(k)
	case TypeSet:
		return r.readSet(address, schema)
	case typeObject:
		return readObjectField(
			&nestedConfigFieldReader{r},
			address, schema.Elem.(map[string]*Schema))
	default:
		panic(fmt.Sprintf("Unknown type: %s", schema.Type))
	}
}

func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) {
	// We want both the raw value and the interpolated. We use the interpolated
	// to store actual values and we use the raw one to check for
	// computed keys. Actual values are obtained in the switch, depending on
	// the type of the raw value.
	mraw, ok := r.Config.GetRaw(k)
	if !ok {
		return FieldReadResult{}, nil
	}

	result := make(map[string]interface{})
	computed := false
	switch m := mraw.(type) {
	case string:
		// This is a map which has come out of an interpolated variable, so we
		// can just get the value directly from config. Values cannot be computed
		// currently.
		v, _ := r.Config.Get(k)

		// If this isn't a map[string]interface, it must be computed.
		mapV, ok := v.(map[string]interface{})
		if !ok {
			return FieldReadResult{
				Exists:   true,
				Computed: true,
			}, nil
		}

		// Otherwise we can proceed as usual.
		for i, iv := range mapV {
			result[i] = iv
		}
	case []interface{}:
		for i, innerRaw := range m {
			for ik := range innerRaw.(map[string]interface{}) {
				key := fmt.Sprintf("%s.%d.%s", k, i, ik)
				if r.Config.IsComputed(key) {
					computed = true
					break
				}

				v, _ := r.Config.Get(key)
				result[ik] = v
			}
		}
	case []map[string]interface{}:
		for i, innerRaw := range m {
			for ik := range innerRaw {
				key := fmt.Sprintf("%s.%d.%s", k, i, ik)
				if r.Config.IsComputed(key) {
					computed = true
					break
				}

				v, _ := r.Config.Get(key)
				result[ik] = v
			}
		}
	case map[string]interface{}:
		for ik := range m {
			key := fmt.Sprintf("%s.%s", k, ik)
			if r.Config.IsComputed(key) {
				computed = true
				break
			}

			v, _ := r.Config.Get(key)
			result[ik] = v
		}
	default:
		panic(fmt.Sprintf("unknown type: %#v", mraw))
	}

	var value interface{}
	if !computed {
		value = result
	}

	return FieldReadResult{
		Value:    value,
		Exists:   true,
		Computed: computed,
	}, nil
}

func (r *ConfigFieldReader) readPrimitive(
	k string, schema *Schema) (FieldReadResult, error) {
	raw, ok := r.Config.Get(k)
	if !ok {
		// Nothing in config, but we might still have a default from the schema
		var err error
		raw, err = schema.DefaultValue()
		if err != nil {
			return FieldReadResult{}, fmt.Errorf("%s, error loading default: %s", k, err)
		}

		if raw == nil {
			return FieldReadResult{}, nil
		}
	}

	var result string
	if err := mapstructure.WeakDecode(raw, &result); err != nil {
		return FieldReadResult{}, err
	}

	computed := r.Config.IsComputed(k)
	returnVal, err := stringToPrimitive(result, computed, schema)
	if err != nil {
		return FieldReadResult{}, err
	}

	return FieldReadResult{
		Value:    returnVal,
		Exists:   true,
		Computed: computed,
	}, nil
}

func (r *ConfigFieldReader) readSet(
	address []string, schema *Schema) (FieldReadResult, error) {
	indexMap := make(map[string]int)
	// Create the set that will be our result
	set := schema.ZeroValue().(*Set)

	raw, err := readListField(&nestedConfigFieldReader{r}, address, schema)
	if err != nil {
		return FieldReadResult{}, err
	}
	if !raw.Exists {
		return FieldReadResult{Value: set}, nil
	}

	// If the list is computed, the set is necessarilly computed
	if raw.Computed {
		return FieldReadResult{
			Value:    set,
			Exists:   true,
			Computed: raw.Computed,
		}, nil
	}

	// Build up the set from the list elements
	for i, v := range raw.Value.([]interface{}) {
		// Check if any of the keys in this item are computed
		computed := r.hasComputedSubKeys(
			fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema)

		code := set.add(v, computed)
		indexMap[code] = i
	}

	r.indexMaps[strings.Join(address, ".")] = indexMap

	return FieldReadResult{
		Value:  set,
		Exists: true,
	}, nil
}

// hasComputedSubKeys walks through a schema and returns whether or not the
// given key contains any subkeys that are computed.
func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool {
	prefix := key + "."

	switch t := schema.Elem.(type) {
	case *Resource:
		for k, schema := range t.Schema {
			addr := prefix + k
			if r.Config.IsComputed(addr) {
				return true
			}

			// We need to loop into sets and lists to ensure we pass the correct
			// address to the raw config - otherwise for sets we get something like
			// set.0.set.item instead of set.0.set.0.item, which renders an
			// inaccurate result.
			if schema.Type == TypeSet || schema.Type == TypeList {
				raw, err := readListField(&nestedConfigFieldReader{r}, strings.Split(addr, "."), schema)
				if err != nil {
					panic(fmt.Errorf("readListField failed when field was supposed to be list-like: %v", err))
				}
				// Just range into the address space here, we don't need the value.
				for i := range raw.Value.([]interface{}) {
					if r.hasComputedSubKeys(addr+"."+strconv.Itoa(i), schema) {
						return true
					}
				}
			} else {
				if r.hasComputedSubKeys(addr, schema) {
					return true
				}
			}
		}
	}

	return false
}

// nestedConfigFieldReader is a funny little thing that just wraps a
// ConfigFieldReader to call readField when ReadField is called so that
// we don't recalculate the set rewrites in the address, which leads to
// an infinite loop.
type nestedConfigFieldReader struct {
	Reader *ConfigFieldReader
}

func (r *nestedConfigFieldReader) ReadField(
	address []string) (FieldReadResult, error) {
	return r.Reader.readField(address, true)
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package main is the main entry point for the app.
package main

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/proto/access"
	"go.chromium.org/luci/config/server/cfgmodule"
	"go.chromium.org/luci/grpc/prpc"
	"go.chromium.org/luci/server"
	"go.chromium.org/luci/server/cron"
	"go.chromium.org/luci/server/gaeemulation"
	"go.chromium.org/luci/server/module"
	"go.chromium.org/luci/server/router"
	"go.chromium.org/luci/server/tq"

	// Enable datastore transactional tasks support.
	_ "go.chromium.org/luci/server/tq/txn/datastore"

	"go.chromium.org/luci/buildbucket/appengine/internal/config"
	"go.chromium.org/luci/buildbucket/appengine/rpc"
	pb "go.chromium.org/luci/buildbucket/proto"
)

// isBeefy returns whether the request was intended for the beefy service.
func isBeefy(req *http.Request) bool {
	return strings.Contains(req.Host, "beefy")
}

// isDev returns whether the request was intended for the dev instance.
func isDev(req *http.Request) bool {
	return strings.HasSuffix(req.Host, "-dev.appspot.com")
}

func main() {
	mods := []module.Module{
		cfgmodule.NewModuleFromFlags(),
		cron.NewModuleFromFlags(),
		gaeemulation.NewModuleFromFlags(),
		tq.NewModuleFromFlags(),
	}

	server.Main(nil, mods, func(srv *server.Server) error {
		// Proxy buildbucket.v2.Builds pRPC requests back to the Python
		// service in order to achieve a programmatic traffic split.
		// Because of the way dispatch routes work, requests are proxied
		// to a copy of the Python service hosted at a different path.
		// TODO(crbug/1042991): Remove the proxy once the go service handles all traffic.
		pythonURL, err := url.Parse(fmt.Sprintf("https://default-dot-%s.appspot.com/python", srv.Options.CloudProject))
		if err != nil {
			panic(err)
		}
		beefyURL, err := url.Parse(fmt.Sprintf("https://beefy-dot-%s.appspot.com/python", srv.Options.CloudProject))
		if err != nil {
			panic(err)
		}
		prx := httputil.NewSingleHostReverseProxy(pythonURL)
		prx.Director = func(req *http.Request) {
			target := pythonURL
			if isBeefy(req) {
				target = beefyURL
			}
			// According to net.Request documentation, setting Host is unnecessary
			// because URL.Host is supposed to be used for outbound requests.
			// However, on GAE, it seems that req.Host is incorrectly used.
			req.Host = target.Host
			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
			req.URL.Path = fmt.Sprintf("%s%s", target.Path, req.URL.Path)
		}
		// makeOverride returns a prpc.Override which allows the given percentage of requests
		// through to this service, proxying the remainder to Python.
		makeOverride := func(prodPct, devPct int) func(*router.Context) bool {
			return func(ctx *router.Context) bool {
				pct := prodPct
				if isDev(ctx.Request) {
					pct = devPct
				}
				switch val := ctx.Request.Header.Get("Should-Proxy"); val {
				case "true":
					pct = 0
					logging.Debugf(ctx.Context, "request demanded to be proxied")
				case "false":
					pct = 100
					logging.Debugf(ctx.Context, "request demanded not to be proxied")
				}
				if mathrand.Intn(ctx.Context, 100) < pct {
					return false
				}
				target := pythonURL
				if isBeefy(ctx.Request) {
					target = beefyURL
				}
				logging.Debugf(ctx.Context, "proxying request to %s", target)
				prx.ServeHTTP(ctx.Writer, ctx.Request)
				return true
			}
		}

		srv.PRPC.AccessControl = prpc.AllowOriginAll
		access.RegisterAccessServer(srv.PRPC, &access.UnimplementedAccessServer{})
		pb.RegisterBuildsServer(srv.PRPC, rpc.NewBuilds())
		pb.RegisterBuildersServer(srv.PRPC, rpc.NewBuilders())
		// TODO(crbug/1082369): Remove this workaround once field masks can be decoded.
		srv.PRPC.HackFixFieldMasksForJSON = true

		// makeOverride(prod % -> Go, dev % -> Go).
		// Does not affect batched ScheduleBuild requests (see rpc/batch.go).
		srv.PRPC.RegisterOverride("buildbucket.v2.Builds", "ScheduleBuild", makeOverride(0, 100))

		cron.RegisterHandler("update_config", config.UpdateSettingsCfg)
		return nil
	})
}
<commit_msg>[buildbucket] Serve 1% of batched/unbatched prod ScheduleBuild requests<commit_after>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package main is the main entry point for the app.
package main

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/proto/access"
	"go.chromium.org/luci/config/server/cfgmodule"
	"go.chromium.org/luci/grpc/prpc"
	"go.chromium.org/luci/server"
	"go.chromium.org/luci/server/cron"
	"go.chromium.org/luci/server/gaeemulation"
	"go.chromium.org/luci/server/module"
	"go.chromium.org/luci/server/router"
	"go.chromium.org/luci/server/tq"

	// Enable datastore transactional tasks support.
	_ "go.chromium.org/luci/server/tq/txn/datastore"

	"go.chromium.org/luci/buildbucket/appengine/internal/config"
	"go.chromium.org/luci/buildbucket/appengine/rpc"
	pb "go.chromium.org/luci/buildbucket/proto"
)

// isBeefy returns whether the request was intended for the beefy service.
func isBeefy(req *http.Request) bool {
	return strings.Contains(req.Host, "beefy")
}

// isDev returns whether the request was intended for the dev instance.
func isDev(req *http.Request) bool {
	return strings.HasSuffix(req.Host, "-dev.appspot.com")
}

func main() {
	mods := []module.Module{
		cfgmodule.NewModuleFromFlags(),
		cron.NewModuleFromFlags(),
		gaeemulation.NewModuleFromFlags(),
		tq.NewModuleFromFlags(),
	}

	server.Main(nil, mods, func(srv *server.Server) error {
		// Proxy buildbucket.v2.Builds pRPC requests back to the Python
		// service in order to achieve a programmatic traffic split.
		// Because of the way dispatch routes work, requests are proxied
		// to a copy of the Python service hosted at a different path.
		// TODO(crbug/1042991): Remove the proxy once the go service handles all traffic.
		pythonURL, err := url.Parse(fmt.Sprintf("https://default-dot-%s.appspot.com/python", srv.Options.CloudProject))
		if err != nil {
			panic(err)
		}
		beefyURL, err := url.Parse(fmt.Sprintf("https://beefy-dot-%s.appspot.com/python", srv.Options.CloudProject))
		if err != nil {
			panic(err)
		}
		prx := httputil.NewSingleHostReverseProxy(pythonURL)
		prx.Director = func(req *http.Request) {
			target := pythonURL
			if isBeefy(req) {
				target = beefyURL
			}
			// According to net.Request documentation, setting Host is unnecessary
			// because URL.Host is supposed to be used for outbound requests.
			// However, on GAE, it seems that req.Host is incorrectly used.
			req.Host = target.Host
			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
			req.URL.Path = fmt.Sprintf("%s%s", target.Path, req.URL.Path)
		}
		// makeOverride returns a prpc.Override which allows the given percentage of requests
		// through to this service, proxying the remainder to Python.
		makeOverride := func(prodPct, devPct int) func(*router.Context) bool {
			return func(ctx *router.Context) bool {
				pct := prodPct
				if isDev(ctx.Request) {
					pct = devPct
				}
				switch val := ctx.Request.Header.Get("Should-Proxy"); val {
				case "true":
					pct = 0
					logging.Debugf(ctx.Context, "request demanded to be proxied")
				case "false":
					pct = 100
					logging.Debugf(ctx.Context, "request demanded not to be proxied")
				}
				if mathrand.Intn(ctx.Context, 100) < pct {
					return false
				}
				target := pythonURL
				if isBeefy(ctx.Request) {
					target = beefyURL
				}
				logging.Debugf(ctx.Context, "proxying request to %s", target)
				prx.ServeHTTP(ctx.Writer, ctx.Request)
				return true
			}
		}

		srv.PRPC.AccessControl = prpc.AllowOriginAll
		access.RegisterAccessServer(srv.PRPC, &access.UnimplementedAccessServer{})
		pb.RegisterBuildsServer(srv.PRPC, rpc.NewBuilds())
		pb.RegisterBuildersServer(srv.PRPC, rpc.NewBuilders())
		// TODO(crbug/1082369): Remove this workaround once field masks can be decoded.
		srv.PRPC.HackFixFieldMasksForJSON = true

		// Percentage of prod ScheduleBuild requests to serve from Go.
		pct := 1
		// Traffic split for unbatched ScheduleBuild requests. makeOverride(prod % -> Go, dev % -> Go).
		srv.PRPC.RegisterOverride("buildbucket.v2.Builds", "ScheduleBuild", makeOverride(pct, 100))
		// Traffic split for batched ScheduleBuild requests in prod (see rpc/batch.go).
		srv.Context = rpc.WithTrafficSplit(srv.Context, pct)

		cron.RegisterHandler("update_config", config.UpdateSettingsCfg)
		return nil
	})
}
<|endoftext|>


<commit_before>package database

import (
	"fmt"
	"time"

	"github.com/hashicorp/vault/builtin/logical/database/dbplugin"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathListRoles(b *databaseBackend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathRoleList(),
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func pathRoles(b *databaseBackend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/" + framework.GenericNameRegex("name"),
		Fields: map[string]*framework.FieldSchema{
			"name": {
				Type:        framework.TypeString,
				Description: "Name of the role.",
			},

			"db_name": {
				Type:        framework.TypeString,
				Description: "Name of the database this role acts on.",
			},
			"creation_statements": {
				Type: framework.TypeString,
				Description: `Statements to be executed to create a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}',
							'{{password}}', and '{{expiration}}' values will be substituted.`,
			},
			"revocation_statements": {
				Type: framework.TypeString,
				Description: `Statements to be executed to revoke a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}' value
							will be substituted.`,
			},
			"renew_statements": {
				Type: framework.TypeString,
				Description: `Statements to be executed to renew a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}' value
							will be substituted.`,
			},
			"rollback_statements": {
				Type: framework.TypeString,
				Description: `SQL statements to be executed to revoke a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}' value
							will be substituted.`,
			},

			"default_ttl": {
				Type:        framework.TypeString,
				Description: "Default ttl for role.",
			},

			"max_ttl": {
				Type:        framework.TypeString,
				Description: "Maximum time a credential is valid for",
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ReadOperation:   b.pathRoleRead(),
			logical.UpdateOperation: b.pathRoleCreate(),
			logical.DeleteOperation: b.pathRoleDelete(),
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func (b *databaseBackend) pathRoleDelete() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		err := req.Storage.Delete("role/" + data.Get("name").(string))
		if err != nil {
			return nil, err
		}

		return nil, nil
	}
}

func (b *databaseBackend) pathRoleRead() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		role, err := b.Role(req.Storage, data.Get("name").(string))
		if err != nil {
			return nil, err
		}
		if role == nil {
			return nil, nil
		}

		return &logical.Response{
			Data: map[string]interface{}{
				"db_name":               role.DBName,
				"creation_statements":   role.Statements.CreationStatements,
				"revocation_statements": role.Statements.RevocationStatements,
				"rollback_statements":   role.Statements.RollbackStatements,
				"renew_statements":      role.Statements.RenewStatements,
				"default_ttl":           role.DefaultTTL.String(),
				"max_ttl":               role.MaxTTL.String(),
			},
		}, nil
	}
}

func (b *databaseBackend) pathRoleList() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		entries, err := req.Storage.List("role/")
		if err != nil {
			return nil, err
		}

		return logical.ListResponse(entries), nil
	}
}

func (b *databaseBackend) pathRoleCreate() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		name := data.Get("name").(string)
		if name == "" {
			return logical.ErrorResponse("empty role name attribute given"), nil
		}

		dbName := data.Get("db_name").(string)
		if dbName == "" {
			return logical.ErrorResponse("empty database name attribute given"), nil
		}

		// Get statements
		creationStmts := data.Get("creation_statements").(string)
		revocationStmts := data.Get("revocation_statements").(string)
		rollbackStmts := data.Get("rollback_statements").(string)
		renewStmts := data.Get("renew_statements").(string)

		// Get TTLs
		defaultTTLRaw := data.Get("default_ttl").(string)
		maxTTLRaw := data.Get("max_ttl").(string)

		defaultTTL, err := time.ParseDuration(defaultTTLRaw)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"invalid default_ttl: %s", err)), nil
		}
		maxTTL, err := time.ParseDuration(maxTTLRaw)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"invalid max_ttl: %s", err)), nil
		}

		statements := dbplugin.Statements{
			CreationStatements:   creationStmts,
			RevocationStatements: revocationStmts,
			RollbackStatements:   rollbackStmts,
			RenewStatements:      renewStmts,
		}

		// Store it
		entry, err := logical.StorageEntryJSON("role/"+name, &roleEntry{
			DBName:     dbName,
			Statements: statements,
			DefaultTTL: defaultTTL,
			MaxTTL:     maxTTL,
		})
		if err != nil {
			return nil, err
		}
		if err := req.Storage.Put(entry); err != nil {
			return nil, err
		}

		return nil, nil
	}
}

type roleEntry struct {
	DBName     string              `json:"db_name" mapstructure:"db_name" structs:"db_name"`
	Statements dbplugin.Statements `json:"statments" mapstructure:"statements" structs:"statments"`
	DefaultTTL time.Duration       `json:"default_ttl" mapstructure:"default_ttl" structs:"default_ttl"`
	MaxTTL     time.Duration       `json:"max_ttl" mapstructure:"max_ttl" structs:"max_ttl"`
}

const pathRoleHelpSyn = `
Manage the roles that can be created with this backend.
`

const pathRoleHelpDesc = `
This path lets you manage the roles that can be created with this backend.

The "db_name" parameter is required and configures the name of the database
connection to use.

The "creation_statements" parameter customizes the string used to create the
credentials. This can be a sequence of SQL queries, or other statement formats
for a particular database type. Some substitution will be done to the statement
strings for certain keys. The names of the variables must be surrounded by "{{"
and "}}" to be replaced.

  * "name" - The random username generated for the DB user.

  * "password" - The random password generated for the DB user.

  * "expiration" - The timestamp when this user will expire.

Example of a decent creation_statements for a postgresql database plugin:

	CREATE ROLE "{{name}}" WITH
	  LOGIN
	  PASSWORD '{{password}}'
	  VALID UNTIL '{{expiration}}';
	GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";

The "revocation_statements" parameter customizes the statement string used to
revoke a user. Example of a decent revocation_statements for a postgresql
database plugin:

	REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM {{name}};
	REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM {{name}};
	REVOKE USAGE ON SCHEMA public FROM {{name}};
	DROP ROLE IF EXISTS {{name}};

The "renew_statements" parameter customizes the statement string used to renew a
user.
The "rollback_statements' parameter customizes the statement string used to
rollback a change if needed.
`
<commit_msg>Change ttl types to TypeDurationSecond<commit_after>package database

import (
	"time"

	"github.com/hashicorp/vault/builtin/logical/database/dbplugin"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathListRoles(b *databaseBackend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathRoleList(),
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func pathRoles(b *databaseBackend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/" + framework.GenericNameRegex("name"),
		Fields: map[string]*framework.FieldSchema{
			"name": {
				Type:        framework.TypeString,
				Description: "Name of the role.",
			},

			"db_name": {
				Type:        framework.TypeString,
				Description: "Name of the database this role acts on.",
			},
			"creation_statements": {
				Type: framework.TypeString,
				Description: `Statements to be executed to create a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}',
							'{{password}}', and '{{expiration}}' values will be substituted.`,
			},
			"revocation_statements": {
				Type: framework.TypeString,
				Description: `Statements to be executed to revoke a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}' value
							will be substituted.`,
			},
			"renew_statements": {
				Type: framework.TypeString,
				Description: `Statements to be executed to renew a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}' value
							will be substituted.`,
			},
			"rollback_statements": {
				Type: framework.TypeString,
				Description: `SQL statements to be executed to revoke a user. Must be a semicolon-separated
							string, a base64-encoded semicolon-separated string, a serialized JSON string
							array, or a base64-encoded serialized JSON string array. The '{{name}}' value
							will be substituted.`,
			},

			"default_ttl": {
				Type:        framework.TypeDurationSecond,
				Description: "Default ttl for role.",
			},

			"max_ttl": {
				Type:        framework.TypeDurationSecond,
				Description: "Maximum time a credential is valid for",
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ReadOperation:   b.pathRoleRead(),
			logical.UpdateOperation: b.pathRoleCreate(),
			logical.DeleteOperation: b.pathRoleDelete(),
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func (b *databaseBackend) pathRoleDelete() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		err := req.Storage.Delete("role/" + data.Get("name").(string))
		if err != nil {
			return nil, err
		}

		return nil, nil
	}
}

func (b *databaseBackend) pathRoleRead() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		role, err := b.Role(req.Storage, data.Get("name").(string))
		if err != nil {
			return nil, err
		}
		if role == nil {
			return nil, nil
		}

		return &logical.Response{
			Data: map[string]interface{}{
				"db_name":               role.DBName,
				"creation_statements":   role.Statements.CreationStatements,
				"revocation_statements": role.Statements.RevocationStatements,
				"rollback_statements":   role.Statements.RollbackStatements,
				"renew_statements":      role.Statements.RenewStatements,
				"default_ttl":           role.DefaultTTL.Seconds(),
				"max_ttl":               role.MaxTTL.Seconds(),
			},
		}, nil
	}
}

func (b *databaseBackend) pathRoleList() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		entries, err := req.Storage.List("role/")
		if err != nil {
			return nil, err
		}

		return logical.ListResponse(entries), nil
	}
}

func (b *databaseBackend) pathRoleCreate() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		name := data.Get("name").(string)
		if name == "" {
			return logical.ErrorResponse("empty role name attribute given"), nil
		}

		dbName := data.Get("db_name").(string)
		if dbName == "" {
			return logical.ErrorResponse("empty database name attribute given"), nil
		}

		// Get statements
		creationStmts := data.Get("creation_statements").(string)
		revocationStmts := data.Get("revocation_statements").(string)
		rollbackStmts := data.Get("rollback_statements").(string)
		renewStmts := data.Get("renew_statements").(string)

		// Get TTLs
		defaultTTLRaw := data.Get("default_ttl").(int)
		maxTTLRaw := data.Get("max_ttl").(int)
		defaultTTL := time.Duration(defaultTTLRaw) * time.Second
		maxTTL := time.Duration(maxTTLRaw) * time.Second

		statements := dbplugin.Statements{
			CreationStatements:   creationStmts,
			RevocationStatements: revocationStmts,
			RollbackStatements:   rollbackStmts,
			RenewStatements:      renewStmts,
		}

		// Store it
		entry, err := logical.StorageEntryJSON("role/"+name, &roleEntry{
			DBName:     dbName,
			Statements: statements,
			DefaultTTL: defaultTTL,
			MaxTTL:     maxTTL,
		})
		if err != nil {
			return nil, err
		}
		if err := req.Storage.Put(entry); err != nil {
			return nil, err
		}

		return nil, nil
	}
}

type roleEntry struct {
	DBName     string              `json:"db_name" mapstructure:"db_name" structs:"db_name"`
	Statements dbplugin.Statements `json:"statments" mapstructure:"statements" structs:"statments"`
	DefaultTTL time.Duration       `json:"default_ttl" mapstructure:"default_ttl" structs:"default_ttl"`
	MaxTTL     time.Duration       `json:"max_ttl" mapstructure:"max_ttl" structs:"max_ttl"`
}

const pathRoleHelpSyn = `
Manage the roles that can be created with this backend.
`

const pathRoleHelpDesc = `
This path lets you manage the roles that can be created with this backend.

The "db_name" parameter is required and configures the name of the database
connection to use.

The "creation_statements" parameter customizes the string used to create the
credentials. This can be a sequence of SQL queries, or other statement formats
for a particular database type. Some substitution will be done to the statement
strings for certain keys. The names of the variables must be surrounded by "{{"
and "}}" to be replaced.

  * "name" - The random username generated for the DB user.

  * "password" - The random password generated for the DB user.

  * "expiration" - The timestamp when this user will expire.

Example of a decent creation_statements for a postgresql database plugin:

	CREATE ROLE "{{name}}" WITH
	  LOGIN
	  PASSWORD '{{password}}'
	  VALID UNTIL '{{expiration}}';
	GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";

The "revocation_statements" parameter customizes the statement string used to
revoke a user. Example of a decent revocation_statements for a postgresql
database plugin:

	REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM {{name}};
	REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM {{name}};
	REVOKE USAGE ON SCHEMA public FROM {{name}};
	DROP ROLE IF EXISTS {{name}};

The "renew_statements" parameter customizes the statement string used to renew a
user.
The "rollback_statements' parameter customizes the statement string used to
rollback a change if needed.
`
<|endoftext|>


<commit_before>package transit

import (
	"context"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/base64"
	"errors"
	"fmt"
	"hash"
	"strconv"
	"strings"
	"time"

	"github.com/google/tink/go/kwp/subtle"
	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/helper/keysutil"
	"github.com/hashicorp/vault/sdk/logical"
)

const EncryptedKeyBytes = 512

func (b *backend) pathImport() *framework.Path {
	return &framework.Path{
		Pattern: "keys/" + framework.GenericNameRegex("name") + "/import",
		Fields: map[string]*framework.FieldSchema{
			"name": {
				Type:        framework.TypeString,
				Description: "The name of the key",
			},
			"type": {
				Type:    framework.TypeString,
				Default: "aes256-gcm96",
				Description: `The type of key being imported. Currently, "aes128-gcm96" (symmetric), "aes256-gcm96" (symmetric), "ecdsa-p256"
(asymmetric), "ecdsa-p384" (asymmetric), "ecdsa-p521" (asymmetric), "ed25519" (asymmetric), "rsa-2048" (asymmetric), "rsa-3072"
(asymmetric), "rsa-4096" (asymmetric) are supported.  Defaults to "aes256-gcm96".
`,
			},
			"hash_function": {
				Type:    framework.TypeString,
				Default: "SHA256",
				Description: `The hash function used as a random oracle in the OAEP wrapping of the user-generated,
ephemeral AES key. Can be one of "SHA1", "SHA224", "SHA256" (default), "SHA384", or "SHA512"`,
			},
			"ciphertext": {
				Type: framework.TypeString,
				Description: `The base64-encoded ciphertext of the keys. The AES key should be encrypted using OAEP
with the wrapping key and then concatenated with the import key, wrapped by the AES key.`,
			},
			"allow_rotation": {
				Type:        framework.TypeBool,
				Description: "True if the imported key may be rotated within Vault; false otherwise.",
			},
			"derived": {
				Type: framework.TypeBool,
				Description: `Enables key derivation mode. This
allows for per-transaction unique
keys for encryption operations.`,
			},

			"exportable": {
				Type: framework.TypeBool,
				Description: `Enables keys to be exportable.
This allows for all the valid keys
in the key ring to be exported.`,
			},

			"allow_plaintext_backup": {
				Type: framework.TypeBool,
				Description: `Enables taking a backup of the named
key in plaintext format. Once set,
this cannot be disabled.`,
			},

			"context": {
				Type: framework.TypeString,
				Description: `Base64 encoded context for key derivation.
When reading a key with key derivation enabled,
if the key type supports public keys, this will
return the public key for the given context.`,
			},
			"auto_rotate_period": {
				Type:    framework.TypeDurationSecond,
				Default: 0,
				Description: `Amount of time the key should live before
being automatically rotated. A value of 0
(default) disables automatic rotation for the
key.`,
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathImportWrite,
		},
		HelpSynopsis:    pathImportWriteSyn,
		HelpDescription: pathImportWriteDesc,
	}
}

func (b *backend) pathImportVersion() *framework.Path {
	return &framework.Path{
		Pattern: "keys/" + framework.GenericNameRegex("name") + "/import_version",
		Fields: map[string]*framework.FieldSchema{
			"name": {
				Type:        framework.TypeString,
				Description: "The name of the key",
			},
			"ciphertext": {
				Type: framework.TypeString,
				Description: `The base64-encoded ciphertext of the keys. The AES key should be encrypted using OAEP
with the wrapping key and then concatenated with the import key, wrapped by the AES key.`,
			},
			"hash_function": {
				Type:    framework.TypeString,
				Default: "SHA256",
				Description: `The hash function used as a random oracle in the OAEP wrapping of the user-generated,
ephemeral AES key. Can be one of "SHA1", "SHA224", "SHA256" (default), "SHA384", or "SHA512"`,
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathImportVersionWrite,
		},
		HelpSynopsis:    pathImportVersionWriteSyn,
		HelpDescription: pathImportVersionWriteDesc,
	}
}

func (b *backend) pathImportWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)
	derived := d.Get("derived").(bool)
	keyType := d.Get("type").(string)
	hashFnStr := d.Get("hash_function").(string)
	exportable := d.Get("exportable").(bool)
	allowPlaintextBackup := d.Get("allow_plaintext_backup").(bool)
	autoRotatePeriod := time.Second * time.Duration(d.Get("auto_rotate_period").(int))
	ciphertextString := d.Get("ciphertext").(string)
	allowRotation := d.Get("allow_rotation").(bool)

	// Ensure the caller didn't supply "convergent_encryption" as a field, since it's not supported on import.
	if _, ok := d.Raw["convergent_encryption"]; ok {
		return nil, errors.New("import cannot be used on keys with convergent encryption enabled")
	}

	if autoRotatePeriod > 0 && !allowRotation {
		return nil, errors.New("allow_rotation must be set to true if auto-rotation is enabled")
	}

	polReq := keysutil.PolicyRequest{
		Storage:                  req.Storage,
		Name:                     name,
		Derived:                  derived,
		Exportable:               exportable,
		AllowPlaintextBackup:     allowPlaintextBackup,
		AutoRotatePeriod:         autoRotatePeriod,
		AllowImportedKeyRotation: allowRotation,
	}

	switch strings.ToLower(keyType) {
	case "aes128-gcm96":
		polReq.KeyType = keysutil.KeyType_AES128_GCM96
	case "aes256-gcm96":
		polReq.KeyType = keysutil.KeyType_AES256_GCM96
	case "chacha20-poly1305":
		polReq.KeyType = keysutil.KeyType_ChaCha20_Poly1305
	case "ecdsa-p256":
		polReq.KeyType = keysutil.KeyType_ECDSA_P256
	case "ecdsa-p384":
		polReq.KeyType = keysutil.KeyType_ECDSA_P384
	case "ecdsa-p521":
		polReq.KeyType = keysutil.KeyType_ECDSA_P521
	case "ed25519":
		polReq.KeyType = keysutil.KeyType_ED25519
	case "rsa-2048":
		polReq.KeyType = keysutil.KeyType_RSA2048
	case "rsa-3072":
		polReq.KeyType = keysutil.KeyType_RSA3072
	case "rsa-4096":
		polReq.KeyType = keysutil.KeyType_RSA4096
	case "hmac":
		polReq.KeyType = keysutil.KeyType_HMAC
	default:
		return logical.ErrorResponse(fmt.Sprintf("unknown key type: %v", keyType)), logical.ErrInvalidRequest
	}

	hashFn, err := parseHashFn(hashFnStr)
	if err != nil {
		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
	}

	p, _, err := b.GetPolicy(ctx, polReq, b.GetRandomReader())
	if err != nil {
		return nil, err
	}

	if p != nil {
		if b.System().CachingDisabled() {
			p.Unlock()
		}
		return nil, errors.New("the import path cannot be used with an existing key; use import-version to rotate an existing imported key")
	}

	ciphertext, err := base64.StdEncoding.DecodeString(ciphertextString)
	if err != nil {
		return nil, err
	}

	key, err := b.decryptImportedKey(ctx, req.Storage, ciphertext, hashFn)
	if err != nil {
		return nil, err
	}

	err = b.lm.ImportPolicy(ctx, polReq, key, b.GetRandomReader())
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) pathImportVersionWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)
	hashFnStr := d.Get("hash_function").(string)
	ciphertextString := d.Get("ciphertext").(string)

	polReq := keysutil.PolicyRequest{
		Storage: req.Storage,
		Name:    name,
		Upsert:  false,
	}

	hashFn, err := parseHashFn(hashFnStr)
	if err != nil {
		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
	}

	p, _, err := b.GetPolicy(ctx, polReq, b.GetRandomReader())
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, fmt.Errorf("no key found with name %s; to import a new key, use the import/ endpoint", name)
	}
	if !p.Imported {
		return nil, errors.New("the import_version endpoint can only be used with an imported key")
	}
	if p.ConvergentEncryption {
		return nil, errors.New("import_version cannot be used on keys with convergent encryption enabled")
	}

	if !b.System().CachingDisabled() {
		p.Lock(true)
	}
	defer p.Unlock()

	ciphertext, err := base64.StdEncoding.DecodeString(ciphertextString)
	if err != nil {
		return nil, err
	}
	importKey, err := b.decryptImportedKey(ctx, req.Storage, ciphertext, hashFn)
	err = p.Import(ctx, req.Storage, importKey, b.GetRandomReader())
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) decryptImportedKey(ctx context.Context, storage logical.Storage, ciphertext []byte, hashFn hash.Hash) ([]byte, error) {
	// Bounds check the ciphertext to avoid panics
	if len(ciphertext) <= EncryptedKeyBytes {
		return nil, errors.New("provided ciphertext is too short")
	}

	wrappedEphKey := ciphertext[:EncryptedKeyBytes]
	wrappedImportKey := ciphertext[EncryptedKeyBytes:]

	wrappingKey, err := b.getWrappingKey(ctx, storage)
	if err != nil {
		return nil, err
	}
	if wrappingKey == nil {
		return nil, fmt.Errorf("error importing key: wrapping key was nil")
	}

	privWrappingKey := wrappingKey.Keys[strconv.Itoa(wrappingKey.LatestVersion)].RSAKey
	ephKey, err := rsa.DecryptOAEP(hashFn, b.GetRandomReader(), privWrappingKey, wrappedEphKey, []byte{})
	if err != nil {
		return nil, err
	}

	// Zero out the ephemeral AES key just to be extra cautious. Note that this
	// isn't a guarantee against memory analysis! See the documentation for the
	// `vault.memzero` utility function for more information.
	defer func() {
		for i := range ephKey {
			ephKey[i] = 0
		}
	}()

	// Ensure the ephemeral AES key is 256-bit
	if len(ephKey) != 32 {
		return nil, errors.New("expected ephemeral AES key to be 256-bit")
	}

	kwp, err := subtle.NewKWP(ephKey)
	if err != nil {
		return nil, err
	}

	importKey, err := kwp.Unwrap(wrappedImportKey)
	if err != nil {
		return nil, err
	}

	return importKey, nil
}

func parseHashFn(hashFn string) (hash.Hash, error) {
	switch strings.ToUpper(hashFn) {
	case "SHA1":
		return sha1.New(), nil
	case "SHA224":
		return sha256.New224(), nil
	case "SHA256":
		return sha256.New(), nil
	case "SHA384":
		return sha512.New384(), nil
	case "SHA512":
		return sha512.New(), nil
	default:
		return nil, fmt.Errorf("unknown hash function: %s", hashFn)
	}
}

const (
	pathImportWriteSyn  = "Imports an externally-generated key into a new transit key"
	pathImportWriteDesc = "This path is used to import an externally-generated " +
		"key into Vault. The import operation creates a new key and cannot be used to " +
		"replace an existing key."
)

const pathImportVersionWriteSyn = "Imports an externally-generated key into an " +
	"existing imported key"

const pathImportVersionWriteDesc = "This path is used to import a new version of an " +
	"externally-generated key into an existing import key. The import_version endpoint " +
	"only supports importing key material into existing imported keys."
<commit_msg>Account for a possible error in Transit BYOK. (#17025)<commit_after>package transit

import (
	"context"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/base64"
	"errors"
	"fmt"
	"hash"
	"strconv"
	"strings"
	"time"

	"github.com/google/tink/go/kwp/subtle"
	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/helper/keysutil"
	"github.com/hashicorp/vault/sdk/logical"
)

const EncryptedKeyBytes = 512

func (b *backend) pathImport() *framework.Path {
	return &framework.Path{
		Pattern: "keys/" + framework.GenericNameRegex("name") + "/import",
		Fields: map[string]*framework.FieldSchema{
			"name": {
				Type:        framework.TypeString,
				Description: "The name of the key",
			},
			"type": {
				Type:    framework.TypeString,
				Default: "aes256-gcm96",
				Description: `The type of key being imported. Currently, "aes128-gcm96" (symmetric), "aes256-gcm96" (symmetric), "ecdsa-p256"
(asymmetric), "ecdsa-p384" (asymmetric), "ecdsa-p521" (asymmetric), "ed25519" (asymmetric), "rsa-2048" (asymmetric), "rsa-3072"
(asymmetric), "rsa-4096" (asymmetric) are supported.  Defaults to "aes256-gcm96".
`,
			},
			"hash_function": {
				Type:    framework.TypeString,
				Default: "SHA256",
				Description: `The hash function used as a random oracle in the OAEP wrapping of the user-generated,
ephemeral AES key. Can be one of "SHA1", "SHA224", "SHA256" (default), "SHA384", or "SHA512"`,
			},
			"ciphertext": {
				Type: framework.TypeString,
				Description: `The base64-encoded ciphertext of the keys. The AES key should be encrypted using OAEP
with the wrapping key and then concatenated with the import key, wrapped by the AES key.`,
			},
			"allow_rotation": {
				Type:        framework.TypeBool,
				Description: "True if the imported key may be rotated within Vault; false otherwise.",
			},
			"derived": {
				Type: framework.TypeBool,
				Description: `Enables key derivation mode. This
allows for per-transaction unique
keys for encryption operations.`,
			},

			"exportable": {
				Type: framework.TypeBool,
				Description: `Enables keys to be exportable.
This allows for all the valid keys
in the key ring to be exported.`,
			},

			"allow_plaintext_backup": {
				Type: framework.TypeBool,
				Description: `Enables taking a backup of the named
key in plaintext format. Once set,
this cannot be disabled.`,
			},

			"context": {
				Type: framework.TypeString,
				Description: `Base64 encoded context for key derivation.
When reading a key with key derivation enabled,
if the key type supports public keys, this will
return the public key for the given context.`,
			},
			"auto_rotate_period": {
				Type:    framework.TypeDurationSecond,
				Default: 0,
				Description: `Amount of time the key should live before
being automatically rotated. A value of 0
(default) disables automatic rotation for the
key.`,
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathImportWrite,
		},
		HelpSynopsis:    pathImportWriteSyn,
		HelpDescription: pathImportWriteDesc,
	}
}

func (b *backend) pathImportVersion() *framework.Path {
	return &framework.Path{
		Pattern: "keys/" + framework.GenericNameRegex("name") + "/import_version",
		Fields: map[string]*framework.FieldSchema{
			"name": {
				Type:        framework.TypeString,
				Description: "The name of the key",
			},
			"ciphertext": {
				Type: framework.TypeString,
				Description: `The base64-encoded ciphertext of the keys. The AES key should be encrypted using OAEP
with the wrapping key and then concatenated with the import key, wrapped by the AES key.`,
			},
			"hash_function": {
				Type:    framework.TypeString,
				Default: "SHA256",
				Description: `The hash function used as a random oracle in the OAEP wrapping of the user-generated,
ephemeral AES key. Can be one of "SHA1", "SHA224", "SHA256" (default), "SHA384", or "SHA512"`,
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathImportVersionWrite,
		},
		HelpSynopsis:    pathImportVersionWriteSyn,
		HelpDescription: pathImportVersionWriteDesc,
	}
}

func (b *backend) pathImportWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)
	derived := d.Get("derived").(bool)
	keyType := d.Get("type").(string)
	hashFnStr := d.Get("hash_function").(string)
	exportable := d.Get("exportable").(bool)
	allowPlaintextBackup := d.Get("allow_plaintext_backup").(bool)
	autoRotatePeriod := time.Second * time.Duration(d.Get("auto_rotate_period").(int))
	ciphertextString := d.Get("ciphertext").(string)
	allowRotation := d.Get("allow_rotation").(bool)

	// Ensure the caller didn't supply "convergent_encryption" as a field, since it's not supported on import.
	if _, ok := d.Raw["convergent_encryption"]; ok {
		return nil, errors.New("import cannot be used on keys with convergent encryption enabled")
	}

	if autoRotatePeriod > 0 && !allowRotation {
		return nil, errors.New("allow_rotation must be set to true if auto-rotation is enabled")
	}

	polReq := keysutil.PolicyRequest{
		Storage:                  req.Storage,
		Name:                     name,
		Derived:                  derived,
		Exportable:               exportable,
		AllowPlaintextBackup:     allowPlaintextBackup,
		AutoRotatePeriod:         autoRotatePeriod,
		AllowImportedKeyRotation: allowRotation,
	}

	switch strings.ToLower(keyType) {
	case "aes128-gcm96":
		polReq.KeyType = keysutil.KeyType_AES128_GCM96
	case "aes256-gcm96":
		polReq.KeyType = keysutil.KeyType_AES256_GCM96
	case "chacha20-poly1305":
		polReq.KeyType = keysutil.KeyType_ChaCha20_Poly1305
	case "ecdsa-p256":
		polReq.KeyType = keysutil.KeyType_ECDSA_P256
	case "ecdsa-p384":
		polReq.KeyType = keysutil.KeyType_ECDSA_P384
	case "ecdsa-p521":
		polReq.KeyType = keysutil.KeyType_ECDSA_P521
	case "ed25519":
		polReq.KeyType = keysutil.KeyType_ED25519
	case "rsa-2048":
		polReq.KeyType = keysutil.KeyType_RSA2048
	case "rsa-3072":
		polReq.KeyType = keysutil.KeyType_RSA3072
	case "rsa-4096":
		polReq.KeyType = keysutil.KeyType_RSA4096
	case "hmac":
		polReq.KeyType = keysutil.KeyType_HMAC
	default:
		return logical.ErrorResponse(fmt.Sprintf("unknown key type: %v", keyType)), logical.ErrInvalidRequest
	}

	hashFn, err := parseHashFn(hashFnStr)
	if err != nil {
		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
	}

	p, _, err := b.GetPolicy(ctx, polReq, b.GetRandomReader())
	if err != nil {
		return nil, err
	}

	if p != nil {
		if b.System().CachingDisabled() {
			p.Unlock()
		}
		return nil, errors.New("the import path cannot be used with an existing key; use import-version to rotate an existing imported key")
	}

	ciphertext, err := base64.StdEncoding.DecodeString(ciphertextString)
	if err != nil {
		return nil, err
	}

	key, err := b.decryptImportedKey(ctx, req.Storage, ciphertext, hashFn)
	if err != nil {
		return nil, err
	}

	err = b.lm.ImportPolicy(ctx, polReq, key, b.GetRandomReader())
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) pathImportVersionWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)
	hashFnStr := d.Get("hash_function").(string)
	ciphertextString := d.Get("ciphertext").(string)

	polReq := keysutil.PolicyRequest{
		Storage: req.Storage,
		Name:    name,
		Upsert:  false,
	}

	hashFn, err := parseHashFn(hashFnStr)
	if err != nil {
		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
	}

	p, _, err := b.GetPolicy(ctx, polReq, b.GetRandomReader())
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, fmt.Errorf("no key found with name %s; to import a new key, use the import/ endpoint", name)
	}
	if !p.Imported {
		return nil, errors.New("the import_version endpoint can only be used with an imported key")
	}
	if p.ConvergentEncryption {
		return nil, errors.New("import_version cannot be used on keys with convergent encryption enabled")
	}

	if !b.System().CachingDisabled() {
		p.Lock(true)
	}
	defer p.Unlock()

	ciphertext, err := base64.StdEncoding.DecodeString(ciphertextString)
	if err != nil {
		return nil, err
	}
	importKey, err := b.decryptImportedKey(ctx, req.Storage, ciphertext, hashFn)
	if err != nil {
		return nil, err
	}
	err = p.Import(ctx, req.Storage, importKey, b.GetRandomReader())
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) decryptImportedKey(ctx context.Context, storage logical.Storage, ciphertext []byte, hashFn hash.Hash) ([]byte, error) {
	// Bounds check the ciphertext to avoid panics
	if len(ciphertext) <= EncryptedKeyBytes {
		return nil, errors.New("provided ciphertext is too short")
	}

	wrappedEphKey := ciphertext[:EncryptedKeyBytes]
	wrappedImportKey := ciphertext[EncryptedKeyBytes:]

	wrappingKey, err := b.getWrappingKey(ctx, storage)
	if err != nil {
		return nil, err
	}
	if wrappingKey == nil {
		return nil, fmt.Errorf("error importing key: wrapping key was nil")
	}

	privWrappingKey := wrappingKey.Keys[strconv.Itoa(wrappingKey.LatestVersion)].RSAKey
	ephKey, err := rsa.DecryptOAEP(hashFn, b.GetRandomReader(), privWrappingKey, wrappedEphKey, []byte{})
	if err != nil {
		return nil, err
	}

	// Zero out the ephemeral AES key just to be extra cautious. Note that this
	// isn't a guarantee against memory analysis! See the documentation for the
	// `vault.memzero` utility function for more information.
	defer func() {
		for i := range ephKey {
			ephKey[i] = 0
		}
	}()

	// Ensure the ephemeral AES key is 256-bit
	if len(ephKey) != 32 {
		return nil, errors.New("expected ephemeral AES key to be 256-bit")
	}

	kwp, err := subtle.NewKWP(ephKey)
	if err != nil {
		return nil, err
	}

	importKey, err := kwp.Unwrap(wrappedImportKey)
	if err != nil {
		return nil, err
	}

	return importKey, nil
}

func parseHashFn(hashFn string) (hash.Hash, error) {
	switch strings.ToUpper(hashFn) {
	case "SHA1":
		return sha1.New(), nil
	case "SHA224":
		return sha256.New224(), nil
	case "SHA256":
		return sha256.New(), nil
	case "SHA384":
		return sha512.New384(), nil
	case "SHA512":
		return sha512.New(), nil
	default:
		return nil, fmt.Errorf("unknown hash function: %s", hashFn)
	}
}

const (
	pathImportWriteSyn  = "Imports an externally-generated key into a new transit key"
	pathImportWriteDesc = "This path is used to import an externally-generated " +
		"key into Vault. The import operation creates a new key and cannot be used to " +
		"replace an existing key."
)

const pathImportVersionWriteSyn = "Imports an externally-generated key into an " +
	"existing imported key"

const pathImportVersionWriteDesc = "This path is used to import a new version of an " +
	"externally-generated key into an existing import key. The import_version endpoint " +
	"only supports importing key material into existing imported keys."
<|endoftext|>


<commit_before>package url

import (
	"encoding/json"
	"fmt"
	"github.com/0x263b/Porygon2"
	"github.com/PuerkitoBio/goquery"
	"github.com/dustin/go-humanize"
	"github.com/kennygrant/sanitize"
	"html"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// 4chan API thread structure
// https://github.com/4chan/4chan-API
type ChanPost struct {
	Posts []struct {
		No  int    `json:"no"`
		Com string `json:"com"`
	} `json:"posts"`
}

// Reddit API thread structure
// https://www.reddit.com/dev/api
type RedditComment []struct {
	Data struct {
		Children []struct {
			Kind string `json:"kind"`
			Data struct {
				Body string `json:"body"`
			} `json:"data"`
		} `json:"children"`
	} `json:"data"`
}

// Used to parse youtube's ISO 8601 durations
// https://en.wikipedia.org/wiki/ISO_8601#Durations
func ParseDuration(str string) time.Duration {
	durationRegex := regexp.MustCompile(`P(?P<years>\d+Y)?(?P<months>\d+M)?(?P<days>\d+D)?T?(?P<hours>\d+H)?(?P<minutes>\d+M)?(?P<seconds>\d+S)?`)
	matches := durationRegex.FindStringSubmatch(str)

	years := ParseInt64(matches[1])
	months := ParseInt64(matches[2])
	days := ParseInt64(matches[3])
	hours := ParseInt64(matches[4])
	minutes := ParseInt64(matches[5])
	seconds := ParseInt64(matches[6])

	hour := int64(time.Hour)
	minute := int64(time.Minute)
	second := int64(time.Second)
	return time.Duration(years*24*365*hour + months*30*24*hour + days*24*hour + hours*hour + minutes*minute + seconds*second)
}

func ParseInt64(value string) int64 {
	if len(value) == 0 {
		return 0
	}
	parsed, err := strconv.Atoi(value[:len(value)-1])
	if err != nil {
		return 0
	}
	return int64(parsed)
}

var timeout = time.Duration(3) * time.Second

func dialTimeout(network, addr string) (net.Conn, error) {
	conn, err := net.DialTimeout(network, addr, timeout)
	return conn, err
}

func extractURL(text string) string {
	extractedURL := ""
	for _, value := range strings.Split(text, " ") {
		parsedURL, err := url.Parse(value)
		if err != nil {
			continue
		}
		if strings.HasPrefix(parsedURL.Scheme, "http") {
			extractedURL = parsedURL.String()
			break
		}
	}
	return extractedURL
}

func openGraphTitle(command *bot.PassiveCmd) (string, error) {
	if !bot.GetChannelKey(command.Channel, "urls") {
		return "", nil
	}

	URL := extractURL(command.Raw)

	if URL == "" {
		return "", nil
	}

	transport := &http.Transport{
		Dial: dialTimeout,
	}

	cookieJar, _ := cookiejar.New(nil)

	client := &http.Client{
		Jar:       cookieJar, // Some sites require cookies to show you anything (nytimes)
		Transport: transport, // Time out if connection hangs
	}

	response, err := client.Get(URL)
	if err != nil {
		return "", err
	}

	finalURL := response.Request.URL.Host

	var bytes int64 = 40960
	// Youtube's html buries the duration at the bottom
	// so we have to read the first 100kB
	if finalURL == "www.youtube.com" {
		bytes = 102400
	}

	if response.Header.Get("Content-Type") == "" {
		// Some servers don't give us anything to work with
		return fmt.Sprintf("Title | (no title) | %s", finalURL), nil
	} else if !strings.Contains(response.Header.Get("Content-Type"), "text/html") {
		if !bot.GetChannelKey(command.Channel, "files") {
			return "", nil
		}
		contentType := response.Header.Get("Content-Type")
		contentLength := response.Header.Get("Content-Length")
		size, _ := strconv.ParseUint(contentLength, 10, 64)
		return fmt.Sprintf("File | %s %s | %s", contentType, humanize.Bytes(size), finalURL), nil
	}

	defer response.Body.Close()
	body := response.Body
	chunk := io.LimitReader(body, bytes) // Download/Read first 20kB

	doc, err := goquery.NewDocumentFromReader(chunk)
	if err != nil {
		fmt.Println(err)
		return "", err
	}

	title := doc.Find("title").Text()

	// Generally <meta> tags have more useful titles
	doc.Find("meta[property='og:title']").Each(func(i int, s *goquery.Selection) {
		title = s.AttrOr("content", title)
	})

	// Get tweet content from <meta>
	if finalURL == "twitter.com" {
		doc.Find("meta[property='og:description']").Each(func(i int, s *goquery.Selection) {
			title = fmt.Sprintf("%s: %s", title, s.AttrOr("content", title))
		})
	}

	// Get video duration from <meta>
	if finalURL == "www.youtube.com" {
		doc.Find("meta[itemprop='duration']").Each(func(i int, s *goquery.Selection) {
			title = fmt.Sprintf("%s | %s", title, ParseDuration(s.AttrOr("content", title)))
		})
	}

	// Get 4chan post
	if finalURL == "boards.4chan.org" {
		if strings.Contains(response.Request.URL.Path, "/thread/") {
			path := strings.Split(response.Request.URL.Path, "/")
			postId := response.Request.URL.Fragment

			response, _ := client.Get(fmt.Sprintf("https://a.4cdn.org/%s/thread/%s.json", path[1], path[3]))

			if response.StatusCode != 200 {
				title = "404 Not Found"
			} else {

				defer response.Body.Close()
				body, _ := ioutil.ReadAll(response.Body)

				var posts ChanPost
				json.Unmarshal(body, &posts)

				title = posts.Posts[0].Com

				if postId != "" {
					for _, element := range posts.Posts {
						if strings.Contains(postId, strconv.Itoa(element.No)) {
							title = element.Com
							break
						}
					}
				}

				title = sanitize.HTML(title) // Remove any unwanted html
			}

			if len(title) < 1 {
				title = "(blank post)"
			}
		}
	}

	if finalURL == "www.reddit.com" {
		thread_title := title
		r := regexp.MustCompile(`\/r\/\w+\/comments\/\w+\/\w+\/\w+`)

		if r.MatchString(response.Request.URL.Path) {

			response, _ := client.Get(fmt.Sprintf("%s.json", URL))

			if response.StatusCode != 200 {
				fmt.Println(response.StatusCode)
				title = "404 Not Found"
			} else {

				defer response.Body.Close()

				body, _ := ioutil.ReadAll(response.Body)
				var comments RedditComment
				json.Unmarshal(body, &comments)

				children := comments[len(comments)-1].Data.Children

				if len(children) > 0 {
					title = children[len(children)-1].Data.Body
				}

				if len(title) < 1 {
					title = thread_title
				}
			}
		}
	}

	reg := regexp.MustCompile("\\s+")
	title = reg.ReplaceAllString(title, " ") // Strip tabs and newlines
	title = strings.TrimSpace(title)         // then trim excessive spaces

	if len(title) > 200 {
		title = fmt.Sprintf("%s …", title[0:200])
	} else if len(title) < 1 {
		title = "(no title)"
	}

	return fmt.Sprintf("Title | %s | %s", html.UnescapeString(title), finalURL), nil
}

func toggleURLs(command *bot.Cmd, matches []string) (msg string, err error) {
	if !bot.IsAdmin(command.Nick) || !bot.IsPrivateMsg(command.Channel, command.Nick) {
		return "", nil
	}

	onOff := matches[1]
	channelToToggle := matches[2]

	if onOff == "on" {
		bot.SetChannelKey(channelToToggle, "urls", true)
		return fmt.Sprintf("Reacting to URLs in %s", channelToToggle), nil
	} else if onOff == "off" {
		bot.SetChannelKey(channelToToggle, "urls", false)
		return fmt.Sprintf("No longer displaying page titles in %s", channelToToggle), nil
	}
	return "", nil
}

func toggleFiles(command *bot.Cmd, matches []string) (msg string, err error) {
	if !bot.IsAdmin(command.Nick) || !bot.IsPrivateMsg(command.Channel, command.Nick) {
		return "", nil
	}

	onOff := matches[1]
	channelToToggle := matches[2]

	if onOff == "on" {
		bot.SetChannelKey(channelToToggle, "files", true)
		return fmt.Sprintf("Reacting to file URLs in %s", channelToToggle), nil
	} else if onOff == "off" {
		bot.SetChannelKey(channelToToggle, "files", false)
		return fmt.Sprintf("No longer displaying file info in %s", channelToToggle), nil
	}
	return "", nil
}

func init() {
	bot.RegisterPassiveCommand(
		"opengraph",
		openGraphTitle)

	bot.RegisterCommand(
		"^set urls (\\S+) (\\S+)$",
		toggleURLs)

	bot.RegisterCommand(
		"^set files (\\S+) (\\S+)$",
		toggleFiles)
}
<commit_msg>remove error log<commit_after>package url

import (
	"encoding/json"
	"fmt"
	"github.com/0x263b/Porygon2"
	"github.com/PuerkitoBio/goquery"
	"github.com/dustin/go-humanize"
	"github.com/kennygrant/sanitize"
	"html"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// 4chan API thread structure
// https://github.com/4chan/4chan-API
type ChanPost struct {
	Posts []struct {
		No  int    `json:"no"`
		Com string `json:"com"`
	} `json:"posts"`
}

// Reddit API thread structure
// https://www.reddit.com/dev/api
type RedditComment []struct {
	Data struct {
		Children []struct {
			Kind string `json:"kind"`
			Data struct {
				Body string `json:"body"`
			} `json:"data"`
		} `json:"children"`
	} `json:"data"`
}

// Used to parse youtube's ISO 8601 durations
// https://en.wikipedia.org/wiki/ISO_8601#Durations
func ParseDuration(str string) time.Duration {
	durationRegex := regexp.MustCompile(`P(?P<years>\d+Y)?(?P<months>\d+M)?(?P<days>\d+D)?T?(?P<hours>\d+H)?(?P<minutes>\d+M)?(?P<seconds>\d+S)?`)
	matches := durationRegex.FindStringSubmatch(str)

	years := ParseInt64(matches[1])
	months := ParseInt64(matches[2])
	days := ParseInt64(matches[3])
	hours := ParseInt64(matches[4])
	minutes := ParseInt64(matches[5])
	seconds := ParseInt64(matches[6])

	hour := int64(time.Hour)
	minute := int64(time.Minute)
	second := int64(time.Second)
	return time.Duration(years*24*365*hour + months*30*24*hour + days*24*hour + hours*hour + minutes*minute + seconds*second)
}

func ParseInt64(value string) int64 {
	if len(value) == 0 {
		return 0
	}
	parsed, err := strconv.Atoi(value[:len(value)-1])
	if err != nil {
		return 0
	}
	return int64(parsed)
}

var timeout = time.Duration(3) * time.Second

func dialTimeout(network, addr string) (net.Conn, error) {
	conn, err := net.DialTimeout(network, addr, timeout)
	return conn, err
}

func extractURL(text string) string {
	extractedURL := ""
	for _, value := range strings.Split(text, " ") {
		parsedURL, err := url.Parse(value)
		if err != nil {
			continue
		}
		if strings.HasPrefix(parsedURL.Scheme, "http") {
			extractedURL = parsedURL.String()
			break
		}
	}
	return extractedURL
}

func openGraphTitle(command *bot.PassiveCmd) (string, error) {
	if !bot.GetChannelKey(command.Channel, "urls") {
		return "", nil
	}

	URL := extractURL(command.Raw)

	if URL == "" {
		return "", nil
	}

	transport := &http.Transport{
		Dial: dialTimeout,
	}

	cookieJar, _ := cookiejar.New(nil)

	client := &http.Client{
		Jar:       cookieJar, // Some sites require cookies to show you anything (nytimes)
		Transport: transport, // Time out if connection hangs
	}

	response, err := client.Get(URL)
	if err != nil {
		return "", err
	}

	finalURL := response.Request.URL.Host

	var bytes int64 = 40960
	// Youtube's html buries the duration at the bottom
	// so we have to read the first 100kB
	if finalURL == "www.youtube.com" {
		bytes = 102400
	}

	if response.Header.Get("Content-Type") == "" {
		// Some servers don't give us anything to work with
		return fmt.Sprintf("Title | (no title) | %s", finalURL), nil
	} else if !strings.Contains(response.Header.Get("Content-Type"), "text/html") {
		if !bot.GetChannelKey(command.Channel, "files") {
			return "", nil
		}
		contentType := response.Header.Get("Content-Type")
		contentLength := response.Header.Get("Content-Length")
		size, _ := strconv.ParseUint(contentLength, 10, 64)
		return fmt.Sprintf("File | %s %s | %s", contentType, humanize.Bytes(size), finalURL), nil
	}

	defer response.Body.Close()
	body := response.Body
	chunk := io.LimitReader(body, bytes) // Download/Read first 20kB

	doc, err := goquery.NewDocumentFromReader(chunk)
	if err != nil {
		return "", err
	}

	title := doc.Find("title").Text()

	// Generally <meta> tags have more useful titles
	doc.Find("meta[property='og:title']").Each(func(i int, s *goquery.Selection) {
		title = s.AttrOr("content", title)
	})

	// Get tweet content from <meta>
	if finalURL == "twitter.com" {
		doc.Find("meta[property='og:description']").Each(func(i int, s *goquery.Selection) {
			title = fmt.Sprintf("%s: %s", title, s.AttrOr("content", title))
		})
	}

	// Get video duration from <meta>
	if finalURL == "www.youtube.com" {
		doc.Find("meta[itemprop='duration']").Each(func(i int, s *goquery.Selection) {
			title = fmt.Sprintf("%s | %s", title, ParseDuration(s.AttrOr("content", title)))
		})
	}

	// Get 4chan post
	if finalURL == "boards.4chan.org" {
		if strings.Contains(response.Request.URL.Path, "/thread/") {
			path := strings.Split(response.Request.URL.Path, "/")
			postId := response.Request.URL.Fragment

			response, _ := client.Get(fmt.Sprintf("https://a.4cdn.org/%s/thread/%s.json", path[1], path[3]))

			if response.StatusCode != 200 {
				title = "404 Not Found"
			} else {

				defer response.Body.Close()
				body, _ := ioutil.ReadAll(response.Body)

				var posts ChanPost
				json.Unmarshal(body, &posts)

				title = posts.Posts[0].Com

				if postId != "" {
					for _, element := range posts.Posts {
						if strings.Contains(postId, strconv.Itoa(element.No)) {
							title = element.Com
							break
						}
					}
				}

				title = sanitize.HTML(title) // Remove any unwanted html
			}

			if len(title) < 1 {
				title = "(blank post)"
			}
		}
	}

	if finalURL == "www.reddit.com" {
		thread_title := title
		r := regexp.MustCompile(`\/r\/\w+\/comments\/\w+\/\w+\/\w+`)

		if r.MatchString(response.Request.URL.Path) {

			response, _ := client.Get(fmt.Sprintf("%s.json", URL))

			if response.StatusCode != 200 {
				title = "404 Not Found"
			} else {

				defer response.Body.Close()

				body, _ := ioutil.ReadAll(response.Body)
				var comments RedditComment
				json.Unmarshal(body, &comments)

				children := comments[len(comments)-1].Data.Children

				if len(children) > 0 {
					title = children[len(children)-1].Data.Body
				}

				if len(title) < 1 {
					title = thread_title
				}
			}
		}
	}

	reg := regexp.MustCompile("\\s+")
	title = reg.ReplaceAllString(title, " ") // Strip tabs and newlines
	title = strings.TrimSpace(title)         // then trim excessive spaces

	if len(title) > 200 {
		title = fmt.Sprintf("%s …", title[0:200])
	} else if len(title) < 1 {
		title = "(no title)"
	}

	return fmt.Sprintf("Title | %s | %s", html.UnescapeString(title), finalURL), nil
}

func toggleURLs(command *bot.Cmd, matches []string) (msg string, err error) {
	if !bot.IsAdmin(command.Nick) || !bot.IsPrivateMsg(command.Channel, command.Nick) {
		return "", nil
	}

	onOff := matches[1]
	channelToToggle := matches[2]

	if onOff == "on" {
		bot.SetChannelKey(channelToToggle, "urls", true)
		return fmt.Sprintf("Reacting to URLs in %s", channelToToggle), nil
	} else if onOff == "off" {
		bot.SetChannelKey(channelToToggle, "urls", false)
		return fmt.Sprintf("No longer displaying page titles in %s", channelToToggle), nil
	}
	return "", nil
}

func toggleFiles(command *bot.Cmd, matches []string) (msg string, err error) {
	if !bot.IsAdmin(command.Nick) || !bot.IsPrivateMsg(command.Channel, command.Nick) {
		return "", nil
	}

	onOff := matches[1]
	channelToToggle := matches[2]

	if onOff == "on" {
		bot.SetChannelKey(channelToToggle, "files", true)
		return fmt.Sprintf("Reacting to file URLs in %s", channelToToggle), nil
	} else if onOff == "off" {
		bot.SetChannelKey(channelToToggle, "files", false)
		return fmt.Sprintf("No longer displaying file info in %s", channelToToggle), nil
	}
	return "", nil
}

func init() {
	bot.RegisterPassiveCommand(
		"opengraph",
		openGraphTitle)

	bot.RegisterCommand(
		"^set urls (\\S+) (\\S+)$",
		toggleURLs)

	bot.RegisterCommand(
		"^set files (\\S+) (\\S+)$",
		toggleFiles)
}
<|endoftext|>


<commit_before>package oauth2

import (
	"github.com/ory-am/fosite"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

type JWTAccessTokenStrategy interface {
	AccessTokenStrategy
	JWTStrategy
}

type StatelessJWTValidator struct {
	JWTAccessTokenStrategy
	ScopeStrategy fosite.ScopeStrategy
}

func (v *StatelessJWTValidator) IntrospectToken(ctx context.Context, token string, tokenType fosite.TokenType, accessRequest fosite.AccessRequester, scopes []string) (err error) {
	or, err := v.JWTAccessTokenStrategy.ValidateJWT(fosite.AccessToken, token)
	if err != nil {
		return err
	}

	for _, scope := range scopes {
		if scope == "" {
			continue
		}

		if !v.ScopeStrategy(or.GetGrantedScopes(), scope) {
			return errors.WithStack(fosite.ErrInvalidScope)
		}
	}

	accessRequest.Merge(or)
	return nil
}
<commit_msg>make stateless validator return an error on revocation<commit_after>package oauth2

import (
	"github.com/ory-am/fosite"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

type JWTAccessTokenStrategy interface {
	AccessTokenStrategy
	JWTStrategy
}

type StatelessJWTValidator struct {
	JWTAccessTokenStrategy
	ScopeStrategy fosite.ScopeStrategy
}

func (v *StatelessJWTValidator) IntrospectToken(ctx context.Context, token string, tokenType fosite.TokenType, accessRequest fosite.AccessRequester, scopes []string) (err error) {
	or, err := v.JWTAccessTokenStrategy.ValidateJWT(fosite.AccessToken, token)
	if err != nil {
		return err
	}

	for _, scope := range scopes {
		if scope == "" {
			continue
		}

		if !v.ScopeStrategy(or.GetGrantedScopes(), scope) {
			return errors.WithStack(fosite.ErrInvalidScope)
		}
	}

	accessRequest.Merge(or)
	return nil
}

// Revocation is not supported with the stateless validator. If you need revocation, use the
// CoreValidator struct instead.
func (v *StatelessJWTValidator) RevokeToken(ctx context.Context, token string, tokenType fosite.TokenType) error {
	return errors.Wrap(fosite.ErrMisconfiguration, "Token revocation is not supported")
}
<|endoftext|>


<commit_before>package repl

import (
	"bytes"
	"log"

	"github.com/gocql/gocql"
	"github.com/gocql/gocqlsh/cql/lexer"

	"github.com/chzyer/readline"
)

type cqlCompleter struct {
	db *gocql.Session
}

func (c *cqlCompleter) Print(prefix string, level int, buf *bytes.Buffer) {
	panic("nope")
}

func (c *cqlCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
	if pos != len(line) {
		// TODO; handle middle of word prediction
		return nil, 0
	}

	lines := c.queryParser(string(line))
	runes := make([][]rune, len(lines))
	for i, line := range lines {
		runes[i] = []rune(line)
	}

	return runes, 0
}

func (c *cqlCompleter) GetName() []rune {
	return []rune("potato")
}

func (c *cqlCompleter) GetChildren() []readline.PrefixCompleterInterface {
	return nil
}

func (c *cqlCompleter) SetChildren(children []readline.PrefixCompleterInterface) {
}

type completer struct {
	l     *lexer.Lexer
	last  lexer.Item
	items []string
}

func (c *completer) Next() bool {
	if len(c.items) > 0 {
		return false
	}

	c.last = c.l.Item()
	if c.last.Typ == lexer.ItemEOF {
		return false
	}
	return true
}

func (c *completer) Space() {
	if len(c.items) > 0 {
		return
	}

	c.Next()

	if c.last.Typ != lexer.ItemWhitespace {
		c.items = append(c.items, " ")
	}
}

func (c *completer) Expect(token string) {
	if len(c.items) > 0 {
		return
	}

	c.Next()

	if c.last.Val != token {
		c.items = append(c.items, token)
	}
}

func (c *completer) Accept(typ lexer.ItemType, fn func() []string) string {
	if len(c.items) > 0 {
		return ""
	}

	c.Next()
	if c.last.Typ != typ {
		c.items = append(c.items, fn()...)
		return ""
	} else {
		return c.last.Val
	}
}

func (c *cqlCompleter) completeInsert(lex *lexer.Lexer) []string {
	comp := &completer{l: lex}

	comp.Space()
	comp.Expect("into")
	comp.Space()

	// TODO: this could be table or keyspace, need to figure out
	keyspace := comp.Accept(lexer.ItemIdentifier, func() []string {
		// TODO: move this to gocql
		var keyspaces []string

		s := c.db.Query("SELECT keyspace_name FROM system.schema_keyspaces").Iter().Scanner()
		for s.Next() {
			var name string
			if err := s.Scan(&name); err != nil {
				log.Println(err)
				return nil
			}

			keyspaces = append(keyspaces, name)
		}

		if err := s.Err(); err != nil {
			log.Println(err)
		}

		return keyspaces
	})

	if keyspace == "" {
		return comp.items
	}

	keyspaceMeta, err := c.db.KeyspaceMetadata(keyspace)
	if err != nil {
		// TODO: need to output errors somewhere
		log.Println(err)
		return comp.items
	}

	comp.Expect(".")
	table := comp.Accept(lexer.ItemIdentifier, func() []string {
		var tables []string
		for table := range keyspaceMeta.Tables {
			tables = append(tables, table)
		}
		return tables
	})

	if table == "" {
		return comp.items
	}

	comp.Expect("(")

	var columns []string

	// column list
	for comp.Next() {
		// could do some better type checking here, ie col [, col]*
		switch comp.last.Typ {
		case lexer.ItemComma:
			comp.Space()
			col := comp.Accept(lexer.ItemIdentifier, func() []string {
				m, ok := keyspaceMeta.Tables[table]
				if !ok {
					return nil
				}

				return m.OrderedColumns
			})

			columns = append(columns, col)
		case lexer.ItemIdentifier:
			comp.Expect(",")
			// TODO: be nice to return ", " here
			comp.Space()
		case lexer.ItemBracket:
			break
		}
	}

	return comp.items
}

func (c *cqlCompleter) queryParser(q string) []string {
	l := lexer.Lex(q)
	keyword := l.ItemNoWS()
	if keyword.Typ != lexer.ItemKeyword {
		log.Printf("%v", keyword)
		return nil
	}

	switch keyword.Val {
	case "select":
		return nil
	case "insert":
		return c.completeInsert(l)
	}

	return prefixComplete(keyword.Val, "insert", "select", "update", "delete")
}
<commit_msg>repl: correctly complete partial terms<commit_after>package repl

import (
	"bytes"
	"log"

	"github.com/gocql/gocql"
	"github.com/gocql/gocqlsh/cql/lexer"

	"github.com/chzyer/readline"
)

type cqlCompleter struct {
	db *gocql.Session
}

func (c *cqlCompleter) Print(prefix string, level int, buf *bytes.Buffer) {
	panic("nope")
}

func (c *cqlCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
	if pos != len(line) {
		// TODO; handle middle of word prediction
		return nil, 0
	}

	lines := c.queryParser(string(line))
	runes := make([][]rune, len(lines))
	for i, line := range lines {
		runes[i] = []rune(line)
	}

	return runes, 0
}

func (c *cqlCompleter) GetName() []rune {
	return []rune("potato")
}

func (c *cqlCompleter) GetChildren() []readline.PrefixCompleterInterface {
	return nil
}

func (c *cqlCompleter) SetChildren(children []readline.PrefixCompleterInterface) {
}

type completer struct {
	l     *lexer.Lexer
	last  lexer.Item
	items []string
}

func (c *completer) Next() bool {
	if len(c.items) > 0 {
		return false
	}

	c.last = c.l.Item()
	if c.last.Typ == lexer.ItemEOF {
		return false
	}
	return true
}

func (c *completer) Space() {
	if len(c.items) > 0 {
		return
	}

	c.Next()

	if c.last.Typ != lexer.ItemWhitespace {
		c.items = append(c.items, " ")
	}
}

func (c *completer) Expect(token string) {
	if len(c.items) > 0 {
		return
	}

	c.Next()

	if c.last.Val != token {
		c.items = append(c.items, token[commonPrefixLen(token, c.last.Val):])
	}
}

func (c *completer) Accept(typ lexer.ItemType, fn func() []string) string {
	if len(c.items) > 0 {
		return ""
	}

	c.Next()
	if c.last.Typ != typ {
		c.items = append(c.items, fn()...)
		return ""
	} else {
		return c.last.Val
	}
}

func (c *cqlCompleter) completeInsert(lex *lexer.Lexer) []string {
	comp := &completer{l: lex}

	comp.Space()
	comp.Expect("into")
	comp.Space()

	// TODO: this could be table or keyspace, need to figure out
	keyspace := comp.Accept(lexer.ItemIdentifier, func() []string {
		// TODO: move this to gocql
		var keyspaces []string

		s := c.db.Query("SELECT keyspace_name FROM system.schema_keyspaces").Iter().Scanner()
		for s.Next() {
			var name string
			if err := s.Scan(&name); err != nil {
				log.Println(err)
				return nil
			}

			keyspaces = append(keyspaces, name)
		}

		if err := s.Err(); err != nil {
			log.Println(err)
		}

		return keyspaces
	})

	if keyspace == "" {
		return comp.items
	}

	keyspaceMeta, err := c.db.KeyspaceMetadata(keyspace)
	if err != nil {
		// TODO: need to output errors somewhere
		log.Println(err)
		return comp.items
	}

	comp.Expect(".")
	table := comp.Accept(lexer.ItemIdentifier, func() []string {
		var tables []string
		for table := range keyspaceMeta.Tables {
			tables = append(tables, table)
		}
		return tables
	})

	if table == "" {
		return comp.items
	}

	comp.Expect("(")

	var columns []string

	// column list
	for comp.Next() {
		// could do some better type checking here, ie col [, col]*
		switch comp.last.Typ {
		case lexer.ItemComma:
			comp.Space()
			col := comp.Accept(lexer.ItemIdentifier, func() []string {
				m, ok := keyspaceMeta.Tables[table]
				if !ok {
					return nil
				}

				return m.OrderedColumns
			})

			columns = append(columns, col)
		case lexer.ItemIdentifier:
			comp.Expect(",")
			// TODO: be nice to return ", " here
			comp.Space()
		case lexer.ItemBracket:
			break
		}
	}

	return comp.items
}

func (c *cqlCompleter) queryParser(q string) []string {
	l := lexer.Lex(q)
	keyword := l.ItemNoWS()
	if keyword.Typ != lexer.ItemKeyword {
		log.Printf("%v", keyword)
		return nil
	}

	switch keyword.Val {
	case "select":
		return nil
	case "insert":
		return c.completeInsert(l)
	}

	return prefixComplete(keyword.Val, "insert", "select", "update", "delete")
}
<|endoftext|>