<commit_before>package token

import (
	"context"
	"gopkg.in/square/go-jose.v2"
	"gopkg.in/square/go-jose.v2/jwt"
	"net/http"
	"time"
	"xorkevin.dev/governor"
)

type (
	// Claims is a set of fields to describe a user
	Claims struct {
		Userid string `json:"userid"`
		ID     string `json:"id"`
		Key    string `json:"key"`
	}

	// Tokenizer is a token generator
	Tokenizer interface {
		Generate(userid string, duration int64, subject, id, key string) (string, *Claims, error)
		Validate(tokenString, subject string) (bool, *Claims)
		GetClaims(tokenString, subject string) (bool, *Claims)
	}

	Service interface {
		governor.Service
		Tokenizer
	}

	service struct {
		secret []byte
		issuer string
		signer jose.Signer
		logger governor.Logger
	}
)

// New creates a new Tokenizer
func New() Service {
	return &service{
		secret: nil,
		issuer: "",
		signer: nil,
	}
}

func (s *service) Register(r governor.ConfigRegistrar, jr governor.JobRegistrar) {
	r.SetDefault("tokensecret", "")
	r.SetDefault("issuer", "governor")
}

func (s *service) Init(ctx context.Context, c governor.Config, r governor.ConfigReader, l governor.Logger, m governor.Router) error {
	s.logger = l
	l = s.logger.WithData(map[string]string{
		"phase": "init",
	})
	tokensecret, err := r.GetSecret("tokensecret")
	if err != nil {
		return governor.NewError("Failed to read token secret", http.StatusInternalServerError, err)
	}
	secret, ok := tokensecret["secret"].(string)
	if !ok {
		return governor.NewError("Invalid secret", http.StatusInternalServerError, nil)
	}
	if secret == "" {
		return governor.NewError("Token secret is not set", http.StatusBadRequest, nil)
	}
	s.secret = []byte(secret)
	issuer := r.GetStr("issuer")
	if issuer == "" {
		return governor.NewError("Token issuer is not set", http.StatusBadRequest, nil)
	}
	s.issuer = issuer
	sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS512, Key: s.secret}, (&jose.SignerOptions{}).WithType("JWT"))
	if err != nil {
		return governor.NewError("Failed to create new jwt signer", http.StatusInternalServerError, err)
	}
	s.signer = sig
	l.Info("loaded config", map[string]string{
		"issuer": issuer,
	})
	return nil
}

func (s *service) Setup(req governor.ReqSetup) error {
	return nil
}

func (s *service) Start(ctx context.Context) error {
	return nil
}

func (s *service) Stop(ctx context.Context) {
}

func (s *service) Health() error {
	return nil
}

// Generate returns a new jwt token from a user model
func (s *service) Generate(userid string, duration int64, subject, id, key string) (string, *Claims, error) {
	now := time.Now().Round(0)
	stdClaims := jwt.Claims{
		Subject:   subject,
		Issuer:    s.issuer,
		IssuedAt:  jwt.NewNumericDate(now),
		NotBefore: jwt.NewNumericDate(now),
		Expiry:    jwt.NewNumericDate(time.Unix(now.Unix()+duration, 0)),
	}
	claims := Claims{
		Userid: userid,
		ID:     id,
		Key:    key,
	}
	token, err := jwt.Signed(s.signer).Claims(stdClaims).Claims(claims).CompactSerialize()
	if err != nil {
		return "", nil, governor.NewError("Failed to generate a new jwt token", http.StatusInternalServerError, err)
	}
	return token, &claims, nil
}

// Validate returns whether a token is valid
func (s *service) Validate(tokenString, subject string) (bool, *Claims) {
	token, err := jwt.ParseSigned(tokenString)
	if err != nil {
		return false, nil
	}
	stdClaims := &jwt.Claims{}
	claims := &Claims{}
	if err := token.Claims(s.secret, stdClaims, claims); err != nil {
		return false, nil
	}
	if err := stdClaims.ValidateWithLeeway(jwt.Expected{
		Subject: subject,
		Issuer:  s.issuer,
	}, 0); err != nil {
		return false, nil
	}
	return true, claims
}

// GetClaims returns the tokens claims without validating time
func (s *service) GetClaims(tokenString, subject string) (bool, *Claims) {
	token, err := jwt.ParseSigned(tokenString)
	if err != nil {
		return false, nil
	}
	stdClaims := &jwt.Claims{}
	claims := &Claims{}
	if err := token.Claims(s.secret, stdClaims, claims); err != nil {
		return false, nil
	}
	if stdClaims.Subject != subject || stdClaims.Issuer != s.issuer {
		return false, nil
	}
	return true, claims
}
<commit_msg>Bugfix use embedded claims in jwt token<commit_after>package token

import (
	"context"
	"gopkg.in/square/go-jose.v2"
	"gopkg.in/square/go-jose.v2/jwt"
	"net/http"
	"time"
	"xorkevin.dev/governor"
)

type (
	// Claims is a set of fields to describe a user
	Claims struct {
		jwt.Claims
		Userid string `json:"userid"`
		ID     string `json:"id"`
		Key    string `json:"key"`
	}

	// Tokenizer is a token generator
	Tokenizer interface {
		Generate(userid string, duration int64, subject, id, key string) (string, *Claims, error)
		Validate(tokenString, subject string) (bool, *Claims)
		GetClaims(tokenString, subject string) (bool, *Claims)
	}

	Service interface {
		governor.Service
		Tokenizer
	}

	service struct {
		secret []byte
		issuer string
		signer jose.Signer
		logger governor.Logger
	}
)

// New creates a new Tokenizer
func New() Service {
	return &service{
		secret: nil,
		issuer: "",
		signer: nil,
	}
}

func (s *service) Register(r governor.ConfigRegistrar, jr governor.JobRegistrar) {
	r.SetDefault("tokensecret", "")
	r.SetDefault("issuer", "governor")
}

func (s *service) Init(ctx context.Context, c governor.Config, r governor.ConfigReader, l governor.Logger, m governor.Router) error {
	s.logger = l
	l = s.logger.WithData(map[string]string{
		"phase": "init",
	})
	tokensecret, err := r.GetSecret("tokensecret")
	if err != nil {
		return governor.NewError("Failed to read token secret", http.StatusInternalServerError, err)
	}
	secret, ok := tokensecret["secret"].(string)
	if !ok {
		return governor.NewError("Invalid secret", http.StatusInternalServerError, nil)
	}
	if secret == "" {
		return governor.NewError("Token secret is not set", http.StatusBadRequest, nil)
	}
	s.secret = []byte(secret)
	issuer := r.GetStr("issuer")
	if issuer == "" {
		return governor.NewError("Token issuer is not set", http.StatusBadRequest, nil)
	}
	s.issuer = issuer
	sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS512, Key: s.secret}, (&jose.SignerOptions{}).WithType("JWT"))
	if err != nil {
		return governor.NewError("Failed to create new jwt signer", http.StatusInternalServerError, err)
	}
	s.signer = sig
	l.Info("loaded config", map[string]string{
		"issuer": issuer,
	})
	return nil
}

func (s *service) Setup(req governor.ReqSetup) error {
	return nil
}

func (s *service) Start(ctx context.Context) error {
	return nil
}

func (s *service) Stop(ctx context.Context) {
}

func (s *service) Health() error {
	return nil
}

// Generate returns a new jwt token from a user model
func (s *service) Generate(userid string, duration int64, subject, id, key string) (string, *Claims, error) {
	now := time.Now().Round(0)
	claims := Claims{
		Claims: jwt.Claims{
			Subject:   subject,
			Issuer:    s.issuer,
			IssuedAt:  jwt.NewNumericDate(now),
			NotBefore: jwt.NewNumericDate(now),
			Expiry:    jwt.NewNumericDate(time.Unix(now.Unix()+duration, 0)),
		},
		Userid: userid,
		ID:     id,
		Key:    key,
	}
	token, err := jwt.Signed(s.signer).Claims(claims).CompactSerialize()
	if err != nil {
		return "", nil, governor.NewError("Failed to generate a new jwt token", http.StatusInternalServerError, err)
	}
	return token, &claims, nil
}

// Validate returns whether a token is valid
func (s *service) Validate(tokenString, subject string) (bool, *Claims) {
	token, err := jwt.ParseSigned(tokenString)
	if err != nil {
		return false, nil
	}
	claims := &Claims{}
	if err := token.Claims(s.secret, claims); err != nil {
		return false, nil
	}
	if err := claims.ValidateWithLeeway(jwt.Expected{
		Subject: subject,
		Issuer:  s.issuer,
	}, 0); err != nil {
		return false, nil
	}
	return true, claims
}

// GetClaims returns the tokens claims without validating time
func (s *service) GetClaims(tokenString, subject string) (bool, *Claims) {
	token, err := jwt.ParseSigned(tokenString)
	if err != nil {
		return false, nil
	}
	claims := &Claims{}
	if err := token.Claims(s.secret, claims); err != nil {
		return false, nil
	}
	if claims.Subject != subject || claims.Issuer != s.issuer {
		return false, nil
	}
	return true, claims
}
<|endoftext|>


<commit_before>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package thumbnail

import (
	"encoding/json"
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/pattern"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/allmark2/common/shutdown"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var dimensionPattern = regexp.MustCompile(`-maxWidth:(\d+)-maxHeight:(\d+)$`)

func NewIndex(logger logger.Logger, indexFilePath string) *Index {

	// assemble the index file path
	index, err := loadIndex(indexFilePath)
	if err != nil {
		logger.Debug("No thumbnail index loaded (%s). Creating a new one.", err.Error())
	}

	// save the index on shutdown
	shutdown.Register(func() error {
		logger.Info("Saving the index")
		return saveIndex(index, indexFilePath)
	})

	return index
}

func emptyIndex() *Index {
	return &Index{
		make(map[string]Thumbs),
	}
}

func loadIndex(indexFilePath string) (*Index, error) {

	if !fsutil.FileExists(indexFilePath) {
		return emptyIndex(), fmt.Errorf("The index file %q does not exist.", indexFilePath)
	}

	// check if file can be accessed
	file, err := os.Open(indexFilePath)
	if err != nil {
		return emptyIndex(), fmt.Errorf("Cannot read index file %q. Error: %s", indexFilePath, err)
	}

	defer file.Close()

	// deserialize the index
	serializer := newIndexSerializer()
	index, err := serializer.DeserializeIndex(file)
	if err != nil {
		return emptyIndex(), fmt.Errorf("Could not deserialize the index file %q. Error: %s", indexFilePath, err)
	}

	return index, nil
}

func saveIndex(index *Index, indexFilePath string) error {
	file, fileError := fsutil.OpenFile(indexFilePath)
	if fileError != nil {
		return fmt.Errorf("Cannot save index to file %q. Error: %s", indexFilePath, fileError.Error())
	}

	defer file.Close()

	// serialize the index
	serializer := newIndexSerializer()
	return serializer.SerializeIndex(file, index)
}

type indexSerializer struct{}

func newIndexSerializer() *indexSerializer {
	return &indexSerializer{}
}

func (indexSerializer) SerializeIndex(writer io.Writer, index *Index) error {
	bytes, err := json.MarshalIndent(index, "", "\t")
	if err != nil {
		return err
	}

	writer.Write(bytes)
	return nil
}

func (indexSerializer) DeserializeIndex(reader io.Reader) (*Index, error) {
	decoder := json.NewDecoder(reader)
	var index Index
	err := decoder.Decode(index)
	return &index, err
}

func newThumb(route route.Route, baseFolder, path string, dimensions ThumbDimension) Thumb {

	return Thumb{
		Route:      route.Value(),
		Path:       path,
		Dimensions: dimensions,
	}

}

type Thumb struct {
	Route      string         `json:"route"`
	BaseFolder string         `json:"baseFolder"`
	Path       string         `json:"path"`
	Dimensions ThumbDimension `json:"dimensions"`
}

func (t Thumb) String() string {
	return fmt.Sprintf("%s (%s)", t.Path, t.Dimensions.String())
}

func (t Thumb) ThumbRoute() route.Route {
	thumbRoute, err := route.NewFromRequest(fmt.Sprintf("%s-%s", t.Route, t.Dimensions.String()))
	if err != nil {
		panic(err)
	}

	return thumbRoute
}

type ThumbDimension struct {
	MaxWidth  uint `json:"maxWidth"`
	MaxHeight uint `json:"maxHeight"`
}

func (t ThumbDimension) String() string {
	return fmt.Sprintf("maxWidth:%v-maxHeight:%v", t.MaxWidth, t.MaxHeight)
}

type Thumbs map[string]Thumb

func (thumbs Thumbs) GetThumbBySize(dimensions ThumbDimension) (Thumb, bool) {
	thumb, exists := thumbs[dimensions.String()]
	return thumb, exists
}

type Index struct {
	Thumbs map[string]Thumbs `json:"thumbs"`
}

func (i *Index) GetThumbs(key string) (thumbs Thumbs, exists bool) {
	thumbs, exists = i.Thumbs[key]
	return thumbs, exists
}

func (i *Index) SetThumbs(key string, thumbs Thumbs) {
	i.Thumbs[key] = thumbs
}

func GetThumbnailDimensionsFromRoute(routeWithDimensions route.Route) (baseRoute route.Route, dimensions ThumbDimension) {
	isMatch, matches := pattern.IsMatch(routeWithDimensions.Value(), dimensionPattern)
	if !isMatch || len(matches) < 3 {
		return routeWithDimensions, ThumbDimension{}
	}

	// width
	widthString := matches[1]
	width, widthError := strconv.ParseUint(widthString, 10, 0)
	if widthError != nil {
		return routeWithDimensions, ThumbDimension{}
	}

	// height
	heightString := matches[2]
	height, heightError := strconv.ParseUint(heightString, 10, 0)
	if heightError != nil {
		return routeWithDimensions, ThumbDimension{}
	}

	// base route
	fullMatch := matches[0]
	cleanedRoute, err := route.NewFromRequest(strings.Replace(routeWithDimensions.Value(), fullMatch, "", 1))
	if err != nil {
		return routeWithDimensions, ThumbDimension{}
	}

	return cleanedRoute, ThumbDimension{
		MaxWidth:  uint(width),
		MaxHeight: uint(height),
	}

}
<commit_msg>Fixed the json deserialization (see: http://stackoverflow.com/questions/20478577/why-does-json-unmarshal-work-with-reference-but-not-pointer)<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package thumbnail

import (
	"encoding/json"
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/pattern"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/allmark2/common/shutdown"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var dimensionPattern = regexp.MustCompile(`-maxWidth:(\d+)-maxHeight:(\d+)$`)

func NewIndex(logger logger.Logger, indexFilePath string) *Index {

	// assemble the index file path
	index, err := loadIndex(indexFilePath)
	if err != nil {
		logger.Debug("No thumbnail index loaded (%s). Creating a new one.", err.Error())
	}

	// save the index on shutdown
	shutdown.Register(func() error {
		logger.Info("Saving the index")
		return saveIndex(index, indexFilePath)
	})

	return index
}

func emptyIndex() *Index {
	return &Index{
		make(map[string]Thumbs),
	}
}

func loadIndex(indexFilePath string) (*Index, error) {

	if !fsutil.FileExists(indexFilePath) {
		return emptyIndex(), fmt.Errorf("The index file %q does not exist.", indexFilePath)
	}

	// check if file can be accessed
	file, err := os.Open(indexFilePath)
	if err != nil {
		return emptyIndex(), fmt.Errorf("Cannot read index file %q. Error: %s", indexFilePath, err)
	}

	defer file.Close()

	// deserialize the index
	serializer := newIndexSerializer()
	index, err := serializer.DeserializeIndex(file)
	if err != nil {
		return emptyIndex(), fmt.Errorf("Could not deserialize the index file %q. Error: %s", indexFilePath, err)
	}

	return index, nil
}

func saveIndex(index *Index, indexFilePath string) error {
	file, fileError := fsutil.OpenFile(indexFilePath)
	if fileError != nil {
		return fmt.Errorf("Cannot save index to file %q. Error: %s", indexFilePath, fileError.Error())
	}

	defer file.Close()

	// serialize the index
	serializer := newIndexSerializer()
	return serializer.SerializeIndex(file, index)
}

type indexSerializer struct{}

func newIndexSerializer() *indexSerializer {
	return &indexSerializer{}
}

func (indexSerializer) SerializeIndex(writer io.Writer, index *Index) error {
	bytes, err := json.MarshalIndent(index, "", "\t")
	if err != nil {
		return err
	}

	writer.Write(bytes)
	return nil
}

func (indexSerializer) DeserializeIndex(reader io.Reader) (*Index, error) {
	decoder := json.NewDecoder(reader)
	var index *Index
	err := decoder.Decode(&index)
	return index, err
}

func newThumb(route route.Route, baseFolder, path string, dimensions ThumbDimension) Thumb {

	return Thumb{
		Route:      route.Value(),
		Path:       path,
		Dimensions: dimensions,
	}

}

type Thumb struct {
	Route      string         `json:"route"`
	BaseFolder string         `json:"baseFolder"`
	Path       string         `json:"path"`
	Dimensions ThumbDimension `json:"dimensions"`
}

func (t Thumb) String() string {
	return fmt.Sprintf("%s (%s)", t.Path, t.Dimensions.String())
}

func (t Thumb) ThumbRoute() route.Route {
	thumbRoute, err := route.NewFromRequest(fmt.Sprintf("%s-%s", t.Route, t.Dimensions.String()))
	if err != nil {
		panic(err)
	}

	return thumbRoute
}

type ThumbDimension struct {
	MaxWidth  uint `json:"maxWidth"`
	MaxHeight uint `json:"maxHeight"`
}

func (t ThumbDimension) String() string {
	return fmt.Sprintf("maxWidth:%v-maxHeight:%v", t.MaxWidth, t.MaxHeight)
}

type Thumbs map[string]Thumb

func (thumbs Thumbs) GetThumbBySize(dimensions ThumbDimension) (Thumb, bool) {
	thumb, exists := thumbs[dimensions.String()]
	return thumb, exists
}

type Index struct {
	Thumbs map[string]Thumbs `json:"thumbs"`
}

func (i *Index) GetThumbs(key string) (thumbs Thumbs, exists bool) {
	thumbs, exists = i.Thumbs[key]
	return thumbs, exists
}

func (i *Index) SetThumbs(key string, thumbs Thumbs) {
	i.Thumbs[key] = thumbs
}

func GetThumbnailDimensionsFromRoute(routeWithDimensions route.Route) (baseRoute route.Route, dimensions ThumbDimension) {
	isMatch, matches := pattern.IsMatch(routeWithDimensions.Value(), dimensionPattern)
	if !isMatch || len(matches) < 3 {
		return routeWithDimensions, ThumbDimension{}
	}

	// width
	widthString := matches[1]
	width, widthError := strconv.ParseUint(widthString, 10, 0)
	if widthError != nil {
		return routeWithDimensions, ThumbDimension{}
	}

	// height
	heightString := matches[2]
	height, heightError := strconv.ParseUint(heightString, 10, 0)
	if heightError != nil {
		return routeWithDimensions, ThumbDimension{}
	}

	// base route
	fullMatch := matches[0]
	cleanedRoute, err := route.NewFromRequest(strings.Replace(routeWithDimensions.Value(), fullMatch, "", 1))
	if err != nil {
		return routeWithDimensions, ThumbDimension{}
	}

	return cleanedRoute, ThumbDimension{
		MaxWidth:  uint(width),
		MaxHeight: uint(height),
	}

}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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 vppcalls_test

import (
	"testing"

	"github.com/ligato/vpp-agent/plugins/vpp/binapi/af_packet"
	"github.com/ligato/vpp-agent/plugins/vpp/binapi/interfaces"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin/vppcalls"
	if_api "github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"github.com/ligato/vpp-agent/tests/vppcallmock"
	. "github.com/onsi/gomega"
)

func TestAddAfPacketInterface(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	ifIndex, err := vppcalls.AddAfPacketInterface("if1", "", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).To(BeNil())
	Expect(ifIndex).ToNot(BeNil())
	Expect(len(ctx.MockChannel.Msgs)).To(BeEquivalentTo(2))
	for i, msg := range ctx.MockChannel.Msgs {
		if i == 0 {
			vppMsg, ok := msg.(*af_packet.AfPacketCreate)
			Expect(ok).To(BeTrue())
			Expect(vppMsg).To(Equal(&af_packet.AfPacketCreate{
				HostIfName:      []byte("host1"),
				HwAddr:          nil,
				UseRandomHwAddr: 1,
			}))
		}
	}
}

func TestAddAfPacketInterfaceError(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketDeleteReply{})

	_, err := vppcalls.AddAfPacketInterface("if1", "", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestAddAfPacketInterfaceRetval(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{
		Retval: 1,
	})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	_, err := vppcalls.AddAfPacketInterface("if1", "", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestDeleteAfPacketInterface(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketDeleteReply{})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	err := vppcalls.DeleteAfPacketInterface("if1", 0, &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).To(BeNil())
	Expect(len(ctx.MockChannel.Msgs)).To(BeEquivalentTo(2))
	for i, msg := range ctx.MockChannel.Msgs {
		if i == 0 {
			vppMsg, ok := msg.(*af_packet.AfPacketDelete)
			Expect(ok).To(BeTrue())
			Expect(vppMsg).To(Equal(&af_packet.AfPacketDelete{
				HostIfName: []byte("host1"),
			}))
		}
	}
}

func TestDeleteAfPacketInterfaceError(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{})

	err := vppcalls.DeleteAfPacketInterface("if1", 0, &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestDeleteAfPacketInterfaceRetval(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketDeleteReply{
		Retval: 1,
	})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	err := vppcalls.DeleteAfPacketInterface("if1", 0, &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}
<commit_msg>Add tests for afpacket_vppcalls MAC<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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 vppcalls_test

import (
	"testing"
	"net"

	"github.com/ligato/vpp-agent/plugins/vpp/binapi/af_packet"
	"github.com/ligato/vpp-agent/plugins/vpp/binapi/interfaces"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin/vppcalls"
	if_api "github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"github.com/ligato/vpp-agent/tests/vppcallmock"
	. "github.com/onsi/gomega"
)

func TestAddAfPacketInterface(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	ifIndex, err := vppcalls.AddAfPacketInterface("if1", "", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).To(BeNil())
	Expect(ifIndex).ToNot(BeNil())
	Expect(len(ctx.MockChannel.Msgs)).To(BeEquivalentTo(2))
	for i, msg := range ctx.MockChannel.Msgs {
		if i == 0 {
			vppMsg, ok := msg.(*af_packet.AfPacketCreate)
			Expect(ok).To(BeTrue())
			Expect(vppMsg).To(Equal(&af_packet.AfPacketCreate{
				HostIfName:      []byte("host1"),
				HwAddr:          nil,
				UseRandomHwAddr: 1,
			}))
		}
	}
}

func TestAddAfPacketInterfaceError(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketDeleteReply{})

	_, err := vppcalls.AddAfPacketInterface("if1", "", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestAddAfPacketInterfaceRetval(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{
		Retval: 1,
	})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	_, err := vppcalls.AddAfPacketInterface("if1", "", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestDeleteAfPacketInterface(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketDeleteReply{})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	err := vppcalls.DeleteAfPacketInterface("if1", 0, &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).To(BeNil())
	Expect(len(ctx.MockChannel.Msgs)).To(BeEquivalentTo(2))
	for i, msg := range ctx.MockChannel.Msgs {
		if i == 0 {
			vppMsg, ok := msg.(*af_packet.AfPacketDelete)
			Expect(ok).To(BeTrue())
			Expect(vppMsg).To(Equal(&af_packet.AfPacketDelete{
				HostIfName: []byte("host1"),
			}))
		}
	}
}

func TestDeleteAfPacketInterfaceError(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{})

	err := vppcalls.DeleteAfPacketInterface("if1", 0, &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestDeleteAfPacketInterfaceRetval(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketDeleteReply{
		Retval: 1,
	})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	err := vppcalls.DeleteAfPacketInterface("if1", 0, &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).ToNot(BeNil())
}

func TestAddAfPacketInterfaceMac(t *testing.T) {
	ctx := vppcallmock.SetupTestCtx(t)
	defer ctx.TeardownTestCtx()

	ctx.MockVpp.MockReply(&af_packet.AfPacketCreateReply{})
	ctx.MockVpp.MockReply(&interfaces.SwInterfaceTagAddDelReply{})

	ifIndex, err := vppcalls.AddAfPacketInterface("if1", "a2:01:01:01:01:01", &if_api.Interfaces_Interface_Afpacket{
		HostIfName: "host1",
	}, ctx.MockChannel, nil)

	Expect(err).To(BeNil())
	Expect(ifIndex).ToNot(BeNil())
	Expect(len(ctx.MockChannel.Msgs)).To(BeEquivalentTo(2))

	mac, err := net.ParseMAC("a2:01:01:01:01:01")
	Expect(err).To(BeNil())

	for i, msg := range ctx.MockChannel.Msgs {
		if i == 0 {
			vppMsg, ok := msg.(*af_packet.AfPacketCreate)
			Expect(ok).To(BeTrue())
			Expect(vppMsg).To(Equal(&af_packet.AfPacketCreate{
				HostIfName:      []byte("host1"),
				HwAddr:          mac,
				UseRandomHwAddr: 0,
			}))
		}
	}
}
<|endoftext|>


<commit_before>// +build linux
// +build cgo

package idmap

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"unsafe"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/shared"
)

// #cgo LDFLAGS: -lacl
/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <byteswap.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/acl.h>

// Needs to be included at the end
#include <sys/xattr.h>

#include "../../lxd/include/memory_utils.h"

#ifndef VFS_CAP_REVISION_1
#define VFS_CAP_REVISION_1 0x01000000
#endif

#ifndef VFS_CAP_REVISION_2
#define VFS_CAP_REVISION_2 0x02000000
#endif

#ifndef VFS_CAP_REVISION_3
#define VFS_CAP_REVISION_3 0x03000000
struct vfs_ns_cap_data {
	__le32 magic_etc;
	struct {
		__le32 permitted;
		__le32 inheritable;
	} data[VFS_CAP_U32];
	__le32 rootid;
};
#endif

#if __BYTE_ORDER == __BIG_ENDIAN
#define BE32_TO_LE32(x) bswap_32(x)
#else
#define BE32_TO_LE32(x) (x)
#endif

int set_vfs_ns_caps(char *path, char *caps, ssize_t len, uint32_t uid)
{
	// Works because vfs_ns_cap_data is a superset of vfs_cap_data (rootid
	// field added to the end)
	struct vfs_ns_cap_data ns_xattr;

	memset(&ns_xattr, 0, sizeof(ns_xattr));
	memcpy(&ns_xattr, caps, len);
	ns_xattr.magic_etc &= ~(VFS_CAP_REVISION_1 | VFS_CAP_REVISION_2);
	ns_xattr.magic_etc |= VFS_CAP_REVISION_3;
	ns_xattr.rootid = BE32_TO_LE32(uid);

	return setxattr(path, "security.capability", &ns_xattr, sizeof(ns_xattr), 0);
}

int set_dummy_fs_ns_caps(const char *path)
{
	#define __raise_cap_permitted(x, ns_cap_data)   ns_cap_data.data[(x)>>5].permitted   |= (1<<((x)&31))

	struct vfs_ns_cap_data ns_xattr;

	memset(&ns_xattr, 0, sizeof(ns_xattr));
        __raise_cap_permitted(CAP_NET_RAW, ns_xattr);
	ns_xattr.magic_etc |= VFS_CAP_REVISION_3 | VFS_CAP_FLAGS_EFFECTIVE;
	ns_xattr.rootid = BE32_TO_LE32(1000000);

	return setxattr(path, "security.capability", &ns_xattr, sizeof(ns_xattr), 0);
}

int shiftowner(char *basepath, char *path, int uid, int gid)
{
	__do_close_prot_errno int fd = -EBADF;
	int ret;
	char fdpath[PATH_MAX], realpath[PATH_MAX];
	struct stat sb;

	fd = open(path, O_PATH | O_NOFOLLOW);
	if (fd < 0) {
		perror("Failed open");
		return 1;
	}

	ret = sprintf(fdpath, "/proc/self/fd/%d", fd);
	if (ret < 0) {
		perror("Failed sprintf");
		return 1;
	}

	ret = readlink(fdpath, realpath, PATH_MAX);
	if (ret < 0) {
		perror("Failed readlink");
		return 1;
	}

	if (strlen(realpath) < strlen(basepath)) {
		printf("Invalid path, source (%s) is outside of basepath (%s)\n", realpath, basepath);
		return 1;
	}

	if (strncmp(realpath, basepath, strlen(basepath))) {
		printf("Invalid path, source (%s) is outside of basepath " "(%s).\n", realpath, basepath);
		return 1;
	}

	ret = fstat(fd, &sb);
	if (ret < 0) {
		perror("Failed fstat");
		return 1;
	}

	ret = fchownat(fd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
	if (ret < 0) {
		perror("Failed chown");
		return 1;
	}

	if (!S_ISLNK(sb.st_mode)) {
		ret = chmod(fdpath, sb.st_mode);
		if (ret < 0) {
			perror("Failed chmod");
			return 1;
		}
	}

	return 0;
}
*/
import "C"

// ShiftOwner updates uid and gid for a file when entering/exiting a namespace
func ShiftOwner(basepath string, path string, uid int, gid int) error {
	cbasepath := C.CString(basepath)
	defer C.free(unsafe.Pointer(cbasepath))

	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	r := C.shiftowner(cbasepath, cpath, C.int(uid), C.int(gid))
	if r != 0 {
		return fmt.Errorf("Failed to change ownership of: %s", path)
	}

	return nil
}

// GetCaps extracts the list of capabilities effective on the file
func GetCaps(path string) ([]byte, error) {
	xattrs, err := shared.GetAllXattr(path)
	if err != nil {
		return nil, err
	}

	valueStr, ok := xattrs["security.capability"]
	if !ok {
		return nil, nil
	}

	return []byte(valueStr), nil
}

// SetCaps applies the caps for a particular root uid
func SetCaps(path string, caps []byte, uid int64) error {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	ccaps := C.CString(string(caps))
	defer C.free(unsafe.Pointer(ccaps))

	r := C.set_vfs_ns_caps(cpath, ccaps, C.ssize_t(len(caps)), C.uint32_t(uid))
	if r != 0 {
		return fmt.Errorf("Failed to apply capabilities to: %s", path)
	}

	return nil
}

// ShiftACL updates uid and gid for file ACLs when entering/exiting a namespace
func ShiftACL(path string, shiftIds func(uid int64, gid int64) (int64, int64)) error {
	err := shiftAclType(path, C.ACL_TYPE_ACCESS, shiftIds)
	if err != nil {
		return err
	}

	err = shiftAclType(path, C.ACL_TYPE_DEFAULT, shiftIds)
	if err != nil {
		return err
	}

	return nil
}

func shiftAclType(path string, aclType int, shiftIds func(uid int64, gid int64) (int64, int64)) error {
	// Convert the path to something usable with cgo
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	// Read the current ACL set for the requested type
	acl := C.acl_get_file(cpath, C.uint(aclType))
	if acl == nil {
		return nil
	}
	defer C.acl_free(unsafe.Pointer(acl))

	newAcl := C.acl_init(0)
	defer C.acl_free(unsafe.Pointer(newAcl))

	// Iterate through all ACL entries
	update := false
	for entryId := C.ACL_FIRST_ENTRY; ; entryId = C.ACL_NEXT_ENTRY {
		var ent C.acl_entry_t
		var newEnt C.acl_entry_t
		var tag C.acl_tag_t

		// Get the ACL entry
		ret := C.acl_get_entry(acl, C.int(entryId), &ent)
		if ret == 0 {
			break
		} else if ret < 0 {
			return fmt.Errorf("Failed to get the ACL entry for %s", path)
		}

		// Setup the new entry
		ret = C.acl_create_entry(&newAcl, &newEnt)
		if ret == -1 {
			return fmt.Errorf("Failed to allocate a new ACL entry for %s", path)
		}

		ret = C.acl_copy_entry(newEnt, ent)
		if ret == -1 {
			return fmt.Errorf("Failed to copy the ACL entry for %s", path)
		}

		// Get the ACL type
		ret = C.acl_get_tag_type(newEnt, &tag)
		if ret == -1 {
			return fmt.Errorf("Failed to get the ACL type for %s", path)
		}

		// We only care about user and group ACLs, copy anything else
		if tag != C.ACL_USER && tag != C.ACL_GROUP {
			continue
		}

		// Get the value
		idp := (*C.id_t)(C.acl_get_qualifier(newEnt))
		if idp == nil {
			return fmt.Errorf("Failed to get current ACL value for %s", path)
		}

		// Shift the value
		newId, _ := shiftIds((int64)(*idp), -1)

		// Update the new entry with the shifted value
		ret = C.acl_set_qualifier(newEnt, unsafe.Pointer(&newId))
		if ret == -1 {
			return fmt.Errorf("Failed to set ACL qualifier on %s", path)
		}

		update = true
	}

	// Update the on-disk ACLs to match
	if update {
		ret := C.acl_set_file(cpath, C.uint(aclType), newAcl)
		if ret == -1 {
			return fmt.Errorf("Failed to change ACLs on %s", path)
		}
	}

	return nil
}

func SupportsVFS3Fscaps(prefix string) bool {
	tmpfile, err := ioutil.TempFile(prefix, ".lxd_fcaps_v3_")
	if err != nil {
		return false
	}
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	err = os.Chmod(tmpfile.Name(), 0001)
	if err != nil {
		return false
	}

	cpath := C.CString(tmpfile.Name())
	defer C.free(unsafe.Pointer(cpath))

	r := C.set_dummy_fs_ns_caps(cpath)
	if r != 0 {
		return false
	}

	cmd := exec.Command(tmpfile.Name())
	err = cmd.Run()
	if err != nil {
		errno, isErrno := shared.GetErrno(err)
		if isErrno && (errno == unix.ERANGE || errno == unix.EOVERFLOW) {
			return false
		}

		return true
	}

	return true
}
<commit_msg>idmap:acl: don't add but update the acls<commit_after>// +build linux
// +build cgo

package idmap

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"unsafe"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/shared"
)

// #cgo LDFLAGS: -lacl
/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <byteswap.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/acl.h>

// Needs to be included at the end
#include <sys/xattr.h>

#include "../../lxd/include/memory_utils.h"

#ifndef VFS_CAP_REVISION_1
#define VFS_CAP_REVISION_1 0x01000000
#endif

#ifndef VFS_CAP_REVISION_2
#define VFS_CAP_REVISION_2 0x02000000
#endif

#ifndef VFS_CAP_REVISION_3
#define VFS_CAP_REVISION_3 0x03000000
struct vfs_ns_cap_data {
	__le32 magic_etc;
	struct {
		__le32 permitted;
		__le32 inheritable;
	} data[VFS_CAP_U32];
	__le32 rootid;
};
#endif

#if __BYTE_ORDER == __BIG_ENDIAN
#define BE32_TO_LE32(x) bswap_32(x)
#else
#define BE32_TO_LE32(x) (x)
#endif

int set_vfs_ns_caps(char *path, char *caps, ssize_t len, uint32_t uid)
{
	// Works because vfs_ns_cap_data is a superset of vfs_cap_data (rootid
	// field added to the end)
	struct vfs_ns_cap_data ns_xattr;

	memset(&ns_xattr, 0, sizeof(ns_xattr));
	memcpy(&ns_xattr, caps, len);
	ns_xattr.magic_etc &= ~(VFS_CAP_REVISION_1 | VFS_CAP_REVISION_2);
	ns_xattr.magic_etc |= VFS_CAP_REVISION_3;
	ns_xattr.rootid = BE32_TO_LE32(uid);

	return setxattr(path, "security.capability", &ns_xattr, sizeof(ns_xattr), 0);
}

int set_dummy_fs_ns_caps(const char *path)
{
	#define __raise_cap_permitted(x, ns_cap_data)   ns_cap_data.data[(x)>>5].permitted   |= (1<<((x)&31))

	struct vfs_ns_cap_data ns_xattr;

	memset(&ns_xattr, 0, sizeof(ns_xattr));
        __raise_cap_permitted(CAP_NET_RAW, ns_xattr);
	ns_xattr.magic_etc |= VFS_CAP_REVISION_3 | VFS_CAP_FLAGS_EFFECTIVE;
	ns_xattr.rootid = BE32_TO_LE32(1000000);

	return setxattr(path, "security.capability", &ns_xattr, sizeof(ns_xattr), 0);
}

int shiftowner(char *basepath, char *path, int uid, int gid)
{
	__do_close_prot_errno int fd = -EBADF;
	int ret;
	char fdpath[PATH_MAX], realpath[PATH_MAX];
	struct stat sb;

	fd = open(path, O_PATH | O_NOFOLLOW);
	if (fd < 0) {
		perror("Failed open");
		return 1;
	}

	ret = sprintf(fdpath, "/proc/self/fd/%d", fd);
	if (ret < 0) {
		perror("Failed sprintf");
		return 1;
	}

	ret = readlink(fdpath, realpath, PATH_MAX);
	if (ret < 0) {
		perror("Failed readlink");
		return 1;
	}

	if (strlen(realpath) < strlen(basepath)) {
		printf("Invalid path, source (%s) is outside of basepath (%s)\n", realpath, basepath);
		return 1;
	}

	if (strncmp(realpath, basepath, strlen(basepath))) {
		printf("Invalid path, source (%s) is outside of basepath " "(%s).\n", realpath, basepath);
		return 1;
	}

	ret = fstat(fd, &sb);
	if (ret < 0) {
		perror("Failed fstat");
		return 1;
	}

	ret = fchownat(fd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
	if (ret < 0) {
		perror("Failed chown");
		return 1;
	}

	if (!S_ISLNK(sb.st_mode)) {
		ret = chmod(fdpath, sb.st_mode);
		if (ret < 0) {
			perror("Failed chmod");
			return 1;
		}
	}

	return 0;
}
*/
import "C"

// ShiftOwner updates uid and gid for a file when entering/exiting a namespace
func ShiftOwner(basepath string, path string, uid int, gid int) error {
	cbasepath := C.CString(basepath)
	defer C.free(unsafe.Pointer(cbasepath))

	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	r := C.shiftowner(cbasepath, cpath, C.int(uid), C.int(gid))
	if r != 0 {
		return fmt.Errorf("Failed to change ownership of: %s", path)
	}

	return nil
}

// GetCaps extracts the list of capabilities effective on the file
func GetCaps(path string) ([]byte, error) {
	xattrs, err := shared.GetAllXattr(path)
	if err != nil {
		return nil, err
	}

	valueStr, ok := xattrs["security.capability"]
	if !ok {
		return nil, nil
	}

	return []byte(valueStr), nil
}

// SetCaps applies the caps for a particular root uid
func SetCaps(path string, caps []byte, uid int64) error {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	ccaps := C.CString(string(caps))
	defer C.free(unsafe.Pointer(ccaps))

	r := C.set_vfs_ns_caps(cpath, ccaps, C.ssize_t(len(caps)), C.uint32_t(uid))
	if r != 0 {
		return fmt.Errorf("Failed to apply capabilities to: %s", path)
	}

	return nil
}

// ShiftACL updates uid and gid for file ACLs when entering/exiting a namespace
func ShiftACL(path string, shiftIds func(uid int64, gid int64) (int64, int64)) error {
	err := shiftAclType(path, C.ACL_TYPE_ACCESS, shiftIds)
	if err != nil {
		return err
	}

	err = shiftAclType(path, C.ACL_TYPE_DEFAULT, shiftIds)
	if err != nil {
		return err
	}

	return nil
}

func shiftAclType(path string, aclType int, shiftIds func(uid int64, gid int64) (int64, int64)) error {
	// Convert the path to something usable with cgo
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	// Read the current ACL set for the requested type
	acl := C.acl_get_file(cpath, C.uint(aclType))
	if acl == nil {
		return nil
	}
	defer C.acl_free(unsafe.Pointer(acl))

	// Iterate through all ACL entries
	update := false
	for entryId := C.ACL_FIRST_ENTRY; ; entryId = C.ACL_NEXT_ENTRY {
		var ent C.acl_entry_t
		var tag C.acl_tag_t

		// Get the ACL entry
		ret := C.acl_get_entry(acl, C.int(entryId), &ent)
		if ret == 0 {
			break
		} else if ret < 0 {
			return fmt.Errorf("Failed to get the ACL entry for %s", path)
		}

		// Get the ACL type
		ret = C.acl_get_tag_type(ent, &tag)
		if ret == -1 {
			return fmt.Errorf("Failed to get the ACL type for %s", path)
		}

		// We only care about user and group ACLs, copy anything else
		if tag != C.ACL_USER && tag != C.ACL_GROUP {
			continue
		}

		// Get the value
		idp := (*C.id_t)(C.acl_get_qualifier(ent))
		if idp == nil {
			return fmt.Errorf("Failed to get current ACL value for %s", path)
		}

		// Shift the value
		newId, _ := shiftIds((int64)(*idp), -1)

		// Update the new entry with the shifted value
		ret = C.acl_set_qualifier(ent, unsafe.Pointer(&newId))
		if ret == -1 {
			return fmt.Errorf("Failed to set ACL qualifier on %s", path)
		}

		update = true
	}

	// Update the on-disk ACLs to match
	if update {
		ret := C.acl_set_file(cpath, C.uint(aclType), acl)
		if ret == -1 {
			return fmt.Errorf("Failed to change ACLs on %s", path)
		}
	}

	return nil
}

func SupportsVFS3Fscaps(prefix string) bool {
	tmpfile, err := ioutil.TempFile(prefix, ".lxd_fcaps_v3_")
	if err != nil {
		return false
	}
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	err = os.Chmod(tmpfile.Name(), 0001)
	if err != nil {
		return false
	}

	cpath := C.CString(tmpfile.Name())
	defer C.free(unsafe.Pointer(cpath))

	r := C.set_dummy_fs_ns_caps(cpath)
	if r != 0 {
		return false
	}

	cmd := exec.Command(tmpfile.Name())
	err = cmd.Run()
	if err != nil {
		errno, isErrno := shared.GetErrno(err)
		if isErrno && (errno == unix.ERANGE || errno == unix.EOVERFLOW) {
			return false
		}

		return true
	}

	return true
}
<|endoftext|>


<commit_before>package validate

import (
	"fmt"
	"net"
	"regexp"
	"strconv"
	"strings"

	"github.com/lxc/lxd/shared/units"
)

// stringInSlice checks whether the supplied string is present in the supplied slice.
func stringInSlice(key string, list []string) bool {
	for _, entry := range list {
		if entry == key {
			return true
		}
	}
	return false
}

// Required returns function that runs one or more validators, all must pass without error.
func Required(validators ...func(value string) error) func(value string) error {
	return func(value string) error {
		for _, validator := range validators {
			err := validator(value)
			if err != nil {
				return err
			}
		}

		return nil
	}
}

// Optional wraps Required() function to make it return nil if value is empty string.
func Optional(validators ...func(value string) error) func(value string) error {
	return func(value string) error {
		if value == "" {
			return nil
		}

		return Required(validators...)(value)
	}
}

// IsInt64 validates whether the string can be converted to an int64.
func IsInt64(value string) error {
	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer %q", value)
	}

	return nil
}

// IsUint8 validates whether the string can be converted to an uint8.
func IsUint8(value string) error {
	_, err := strconv.ParseUint(value, 10, 8)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer %q. Must be between 0 and 255", value)
	}

	return nil
}

// IsUint32 validates whether the string can be converted to an uint32.
func IsUint32(value string) error {
	_, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid value for uint32 %q: %v", value, err)
	}

	return nil
}

// IsPriority validates priority number.
func IsPriority(value string) error {
	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer %q", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit %q. Must be between 0 and 10", value)
	}

	return nil
}

// IsBool validates if string can be understood as a bool.
func IsBool(value string) error {
	if !stringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean %q", value)
	}

	return nil
}

// IsOneOf checks whether the string is present in the supplied slice of strings.
func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !stringInSlice(value, valid) {
		return fmt.Errorf("Invalid value %q (not one of %s)", value, valid)
	}

	return nil
}

// IsAny accepts all strings as valid.
func IsAny(value string) error {
	return nil
}

// IsNotEmpty requires a non-empty string.
func IsNotEmpty(value string) error {
	if value == "" {
		return fmt.Errorf("Required value")
	}

	return nil
}

// IsSize checks if string is valid size according to units.ParseByteSizeString.
func IsSize(value string) error {
	_, err := units.ParseByteSizeString(value)
	if err != nil {
		return err
	}

	return nil
}

// IsDeviceID validates string is four lowercase hex characters suitable as Vendor or Device ID.
func IsDeviceID(value string) error {
	regexHexLc, err := regexp.Compile("^[0-9a-f]+$")
	if err != nil {
		return err
	}

	if len(value) != 4 || !regexHexLc.MatchString(value) {
		return fmt.Errorf("Invalid value, must be four lower case hex characters")
	}

	return nil
}

// IsNetworkMAC validates an Ethernet MAC address. e.g. "00:00:5e:00:53:01".
func IsNetworkMAC(value string) error {
	_, err := net.ParseMAC(value)

	// Check is valid Ethernet MAC length and delimiter.
	if err != nil || len(value) != 17 || strings.ContainsAny(value, "-.") {
		return fmt.Errorf("Invalid MAC address, must be 6 bytes of hex separated by colons")
	}

	return nil
}

// IsNetworkAddress validates an IP (v4 or v6) address string. If string is empty, returns valid.
func IsNetworkAddress(value string) error {
	ip := net.ParseIP(value)
	if ip == nil {
		return fmt.Errorf("Not an IP address %q", value)
	}

	return nil
}

// IsNetworkAddressList validates a comma delimited list of IPv4 or IPv6 addresses.
func IsNetworkAddressList(value string) error {
	for _, v := range strings.Split(value, ",") {
		v = strings.TrimSpace(v)
		err := IsNetworkAddress(v)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkV4 validates an IPv4 CIDR string. If string is empty, returns valid.
func IsNetworkV4(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip.To4() == nil {
		return fmt.Errorf("Not an IPv4 network %q", value)
	}

	if ip.String() != subnet.IP.String() {
		return fmt.Errorf("Not an IPv4 network address %q", value)
	}

	return nil
}

// IsNetworkAddressV4 validates an IPv4 addresss string. If string is empty, returns valid.
func IsNetworkAddressV4(value string) error {
	ip := net.ParseIP(value)
	if ip == nil || ip.To4() == nil {
		return fmt.Errorf("Not an IPv4 address %q", value)
	}

	return nil
}

// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format. If string is empty, returns valid.
func IsNetworkAddressCIDRV4(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip.To4() == nil {
		return fmt.Errorf("Not an IPv4 address %q", value)
	}

	if ip.String() == subnet.IP.String() {
		return fmt.Errorf("Not a usable IPv4 address %q", value)
	}

	return nil
}

// IsNetworkAddressV4List validates a comma delimited list of IPv4 addresses.
func IsNetworkAddressV4List(value string) error {
	for _, v := range strings.Split(value, ",") {
		v = strings.TrimSpace(v)
		err := IsNetworkAddressV4(v)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkV4List validates a comma delimited list of IPv4 CIDR strings.
func IsNetworkV4List(value string) error {
	for _, network := range strings.Split(value, ",") {
		network = strings.TrimSpace(network)
		err := IsNetworkV4(network)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkV6 validates an IPv6 CIDR string. If string is empty, returns valid.
func IsNetworkV6(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip == nil || ip.To4() != nil {
		return fmt.Errorf("Not an IPv6 network %q", value)
	}

	if ip.String() != subnet.IP.String() {
		return fmt.Errorf("Not an IPv6 network address %q", value)
	}

	return nil
}

// IsNetworkAddressV6 validates an IPv6 addresss string. If string is empty, returns valid.
func IsNetworkAddressV6(value string) error {
	ip := net.ParseIP(value)
	if ip == nil || ip.To4() != nil {
		return fmt.Errorf("Not an IPv6 address %q", value)
	}

	return nil
}

// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. If string is empty, returns valid.
func IsNetworkAddressCIDRV6(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip.To4() != nil {
		return fmt.Errorf("Not an IPv6 address %q", value)
	}

	if ip.String() == subnet.IP.String() {
		return fmt.Errorf("Not a usable IPv6 address %q", value)
	}

	return nil
}

// IsNetworkAddressV6List validates a comma delimited list of IPv6 addresses.
func IsNetworkAddressV6List(value string) error {
	for _, v := range strings.Split(value, ",") {
		v = strings.TrimSpace(v)
		err := IsNetworkAddressV6(v)
		if err != nil {
			return err
		}
	}
	return nil
}

// IsNetworkV6List validates a comma delimited list of IPv6 CIDR strings.
func IsNetworkV6List(value string) error {
	for _, network := range strings.Split(value, ",") {
		network = strings.TrimSpace(network)
		err := IsNetworkV6(network)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV4 validates an IPv4 range in the format "start-end".
func IsNetworkRangeV4(value string) error {
	ips := strings.SplitN(value, "-", 2)
	if len(ips) != 2 {
		return fmt.Errorf("IP range must contain start and end IP addresses")
	}

	for _, ip := range ips {
		err := IsNetworkAddressV4(ip)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV4List validates a comma delimited list of IPv4 ranges.
func IsNetworkRangeV4List(value string) error {
	for _, ipRange := range strings.Split(value, ",") {
		err := IsNetworkRangeV4(strings.TrimSpace(ipRange))
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV6 validates an IPv6 range in the format "start-end".
func IsNetworkRangeV6(value string) error {
	ips := strings.SplitN(value, "-", 2)
	if len(ips) != 2 {
		return fmt.Errorf("IP range must contain start and end IP addresses")
	}

	for _, ip := range ips {
		err := IsNetworkAddressV6(ip)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV6List validates a comma delimited list of IPv6 ranges.
func IsNetworkRangeV6List(value string) error {
	for _, ipRange := range strings.Split(value, ",") {
		err := IsNetworkRangeV6(strings.TrimSpace(ipRange))
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkVLAN validates a VLAN ID.
func IsNetworkVLAN(value string) error {
	vlanID, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Invalid VLAN ID %q", value)
	}

	if vlanID < 0 || vlanID > 4094 {
		return fmt.Errorf("Out of VLAN ID range (0-4094) %q", value)
	}

	return nil
}

// IsNetworkMTU validates MTU number >= 1280 and <= 16384.
// Anything below 68 and the kernel doesn't allow IPv4, anything below 1280 and the kernel doesn't allow IPv6.
// So require an IPv6-compatible MTU as the low value and cap at the max ethernet jumbo frame size.
func IsNetworkMTU(value string) error {
	mtu, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid MTU %q", value)
	}

	if mtu < 1280 || mtu > 16384 {
		return fmt.Errorf("Out of MTU range (1280-16384) %q", value)
	}

	return nil
}

// IsURLSegmentSafe validates whether value can be used in a URL segment.
func IsURLSegmentSafe(value string) error {
	for _, char := range []string{"/", "?", "&", "+"} {
		if strings.Contains(value, char) {
			return fmt.Errorf("Cannot contain %q", char)
		}
	}

	return nil
}
<commit_msg>shared/validate/validate: Removes inaccurate comments about optional values<commit_after>package validate

import (
	"fmt"
	"net"
	"regexp"
	"strconv"
	"strings"

	"github.com/lxc/lxd/shared/units"
)

// stringInSlice checks whether the supplied string is present in the supplied slice.
func stringInSlice(key string, list []string) bool {
	for _, entry := range list {
		if entry == key {
			return true
		}
	}
	return false
}

// Required returns function that runs one or more validators, all must pass without error.
func Required(validators ...func(value string) error) func(value string) error {
	return func(value string) error {
		for _, validator := range validators {
			err := validator(value)
			if err != nil {
				return err
			}
		}

		return nil
	}
}

// Optional wraps Required() function to make it return nil if value is empty string.
func Optional(validators ...func(value string) error) func(value string) error {
	return func(value string) error {
		if value == "" {
			return nil
		}

		return Required(validators...)(value)
	}
}

// IsInt64 validates whether the string can be converted to an int64.
func IsInt64(value string) error {
	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer %q", value)
	}

	return nil
}

// IsUint8 validates whether the string can be converted to an uint8.
func IsUint8(value string) error {
	_, err := strconv.ParseUint(value, 10, 8)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer %q. Must be between 0 and 255", value)
	}

	return nil
}

// IsUint32 validates whether the string can be converted to an uint32.
func IsUint32(value string) error {
	_, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid value for uint32 %q: %v", value, err)
	}

	return nil
}

// IsPriority validates priority number.
func IsPriority(value string) error {
	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer %q", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit %q. Must be between 0 and 10", value)
	}

	return nil
}

// IsBool validates if string can be understood as a bool.
func IsBool(value string) error {
	if !stringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean %q", value)
	}

	return nil
}

// IsOneOf checks whether the string is present in the supplied slice of strings.
func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !stringInSlice(value, valid) {
		return fmt.Errorf("Invalid value %q (not one of %s)", value, valid)
	}

	return nil
}

// IsAny accepts all strings as valid.
func IsAny(value string) error {
	return nil
}

// IsNotEmpty requires a non-empty string.
func IsNotEmpty(value string) error {
	if value == "" {
		return fmt.Errorf("Required value")
	}

	return nil
}

// IsSize checks if string is valid size according to units.ParseByteSizeString.
func IsSize(value string) error {
	_, err := units.ParseByteSizeString(value)
	if err != nil {
		return err
	}

	return nil
}

// IsDeviceID validates string is four lowercase hex characters suitable as Vendor or Device ID.
func IsDeviceID(value string) error {
	regexHexLc, err := regexp.Compile("^[0-9a-f]+$")
	if err != nil {
		return err
	}

	if len(value) != 4 || !regexHexLc.MatchString(value) {
		return fmt.Errorf("Invalid value, must be four lower case hex characters")
	}

	return nil
}

// IsNetworkMAC validates an Ethernet MAC address. e.g. "00:00:5e:00:53:01".
func IsNetworkMAC(value string) error {
	_, err := net.ParseMAC(value)

	// Check is valid Ethernet MAC length and delimiter.
	if err != nil || len(value) != 17 || strings.ContainsAny(value, "-.") {
		return fmt.Errorf("Invalid MAC address, must be 6 bytes of hex separated by colons")
	}

	return nil
}

// IsNetworkAddress validates an IP (v4 or v6) address string.
func IsNetworkAddress(value string) error {
	ip := net.ParseIP(value)
	if ip == nil {
		return fmt.Errorf("Not an IP address %q", value)
	}

	return nil
}

// IsNetworkAddressList validates a comma delimited list of IPv4 or IPv6 addresses.
func IsNetworkAddressList(value string) error {
	for _, v := range strings.Split(value, ",") {
		v = strings.TrimSpace(v)
		err := IsNetworkAddress(v)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkV4 validates an IPv4 CIDR string.
func IsNetworkV4(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip.To4() == nil {
		return fmt.Errorf("Not an IPv4 network %q", value)
	}

	if ip.String() != subnet.IP.String() {
		return fmt.Errorf("Not an IPv4 network address %q", value)
	}

	return nil
}

// IsNetworkAddressV4 validates an IPv4 addresss string.
func IsNetworkAddressV4(value string) error {
	ip := net.ParseIP(value)
	if ip == nil || ip.To4() == nil {
		return fmt.Errorf("Not an IPv4 address %q", value)
	}

	return nil
}

// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format.
func IsNetworkAddressCIDRV4(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip.To4() == nil {
		return fmt.Errorf("Not an IPv4 address %q", value)
	}

	if ip.String() == subnet.IP.String() {
		return fmt.Errorf("Not a usable IPv4 address %q", value)
	}

	return nil
}

// IsNetworkAddressV4List validates a comma delimited list of IPv4 addresses.
func IsNetworkAddressV4List(value string) error {
	for _, v := range strings.Split(value, ",") {
		v = strings.TrimSpace(v)
		err := IsNetworkAddressV4(v)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkV4List validates a comma delimited list of IPv4 CIDR strings.
func IsNetworkV4List(value string) error {
	for _, network := range strings.Split(value, ",") {
		network = strings.TrimSpace(network)
		err := IsNetworkV4(network)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkV6 validates an IPv6 CIDR string.
func IsNetworkV6(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip == nil || ip.To4() != nil {
		return fmt.Errorf("Not an IPv6 network %q", value)
	}

	if ip.String() != subnet.IP.String() {
		return fmt.Errorf("Not an IPv6 network address %q", value)
	}

	return nil
}

// IsNetworkAddressV6 validates an IPv6 addresss string.
func IsNetworkAddressV6(value string) error {
	ip := net.ParseIP(value)
	if ip == nil || ip.To4() != nil {
		return fmt.Errorf("Not an IPv6 address %q", value)
	}

	return nil
}

// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format.
func IsNetworkAddressCIDRV6(value string) error {
	ip, subnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	if ip.To4() != nil {
		return fmt.Errorf("Not an IPv6 address %q", value)
	}

	if ip.String() == subnet.IP.String() {
		return fmt.Errorf("Not a usable IPv6 address %q", value)
	}

	return nil
}

// IsNetworkAddressV6List validates a comma delimited list of IPv6 addresses.
func IsNetworkAddressV6List(value string) error {
	for _, v := range strings.Split(value, ",") {
		v = strings.TrimSpace(v)
		err := IsNetworkAddressV6(v)
		if err != nil {
			return err
		}
	}
	return nil
}

// IsNetworkV6List validates a comma delimited list of IPv6 CIDR strings.
func IsNetworkV6List(value string) error {
	for _, network := range strings.Split(value, ",") {
		network = strings.TrimSpace(network)
		err := IsNetworkV6(network)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV4 validates an IPv4 range in the format "start-end".
func IsNetworkRangeV4(value string) error {
	ips := strings.SplitN(value, "-", 2)
	if len(ips) != 2 {
		return fmt.Errorf("IP range must contain start and end IP addresses")
	}

	for _, ip := range ips {
		err := IsNetworkAddressV4(ip)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV4List validates a comma delimited list of IPv4 ranges.
func IsNetworkRangeV4List(value string) error {
	for _, ipRange := range strings.Split(value, ",") {
		err := IsNetworkRangeV4(strings.TrimSpace(ipRange))
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV6 validates an IPv6 range in the format "start-end".
func IsNetworkRangeV6(value string) error {
	ips := strings.SplitN(value, "-", 2)
	if len(ips) != 2 {
		return fmt.Errorf("IP range must contain start and end IP addresses")
	}

	for _, ip := range ips {
		err := IsNetworkAddressV6(ip)
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkRangeV6List validates a comma delimited list of IPv6 ranges.
func IsNetworkRangeV6List(value string) error {
	for _, ipRange := range strings.Split(value, ",") {
		err := IsNetworkRangeV6(strings.TrimSpace(ipRange))
		if err != nil {
			return err
		}
	}

	return nil
}

// IsNetworkVLAN validates a VLAN ID.
func IsNetworkVLAN(value string) error {
	vlanID, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Invalid VLAN ID %q", value)
	}

	if vlanID < 0 || vlanID > 4094 {
		return fmt.Errorf("Out of VLAN ID range (0-4094) %q", value)
	}

	return nil
}

// IsNetworkMTU validates MTU number >= 1280 and <= 16384.
// Anything below 68 and the kernel doesn't allow IPv4, anything below 1280 and the kernel doesn't allow IPv6.
// So require an IPv6-compatible MTU as the low value and cap at the max ethernet jumbo frame size.
func IsNetworkMTU(value string) error {
	mtu, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid MTU %q", value)
	}

	if mtu < 1280 || mtu > 16384 {
		return fmt.Errorf("Out of MTU range (1280-16384) %q", value)
	}

	return nil
}

// IsURLSegmentSafe validates whether value can be used in a URL segment.
func IsURLSegmentSafe(value string) error {
	for _, char := range []string{"/", "?", "&", "+"} {
		if strings.Contains(value, char) {
			return fmt.Errorf("Cannot contain %q", char)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// (c) Copyright 2015-2017 JONNALAGADDA Srinivas
//
// 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 flow

// NodeType enumerates the possible types of workflow nodes.
type NodeType string

// The following constants are represented **identically** as part of
// an enumeration in the database.  DO NOT ALTER THESE WITHOUT ALSO
// ALTERING THE DATABASE; ELSE DATA COULD GET CORRUPTED!
const (
	// NodeTypeBegin : none incoming, one outgoing
	NodeTypeBegin NodeType = "begin"
	// NodeTypeEnd : one incoming, none outgoing
	NodeTypeEnd = "end"
	// NodeTypeLinear : one incoming, one outgoing
	NodeTypeLinear = "linear"
	// NodeTypeBranch : one incoming, two or more outgoing
	NodeTypeBranch = "branch"
	// NodeTypeJoinAny : two or more incoming, one outgoing
	NodeTypeJoinAny = "joinany"
	// NodeTypeJoinAll : two or more incoming, one outgoing
	NodeTypeJoinAll = "joinall"
)
<commit_msg>Add a validator for node type strings<commit_after>// (c) Copyright 2015-2017 JONNALAGADDA Srinivas
//
// 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 flow

// NodeType enumerates the possible types of workflow nodes.
type NodeType string

// The following constants are represented **identically** as part of
// an enumeration in the database.  DO NOT ALTER THESE WITHOUT ALSO
// ALTERING THE DATABASE; ELSE DATA COULD GET CORRUPTED!
const (
	// NodeTypeBegin : none incoming, one outgoing
	NodeTypeBegin NodeType = "begin"
	// NodeTypeEnd : one incoming, none outgoing
	NodeTypeEnd = "end"
	// NodeTypeLinear : one incoming, one outgoing
	NodeTypeLinear = "linear"
	// NodeTypeBranch : one incoming, two or more outgoing
	NodeTypeBranch = "branch"
	// NodeTypeJoinAny : two or more incoming, one outgoing
	NodeTypeJoinAny = "joinany"
	// NodeTypeJoinAll : two or more incoming, one outgoing
	NodeTypeJoinAll = "joinall"
)

// IsValidNodeType answers `true` if the given node type is a
// recognised node type in the system.
func IsValidNodeType(ntype string) bool {
	nt := NodeType(ntype)
	switch nt {
	case NodeTypeBegin, NodeTypeEnd, NodeTypeLinear, NodeTypeBranch, NodeTypeJoinAny, NodeTypeJoinAll:
		return true

	default:
		return false
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"testing"
)

////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////

type fakeMatcher struct {
	matchFunc func(interface{}) (MatchResult, string)
	description string
}

func (m *fakeMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.matchFunc(c)
}

func (m *fakeMatcher) Description() string {
	return m.description
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestCallsWrapped(t *testing.T) {
	var suppliedCandidate interface{}
	matchFunc := func(c interface{}) (MatchResult, string) {
		suppliedCandidate = c
		return MATCH_TRUE, ""
	}

	wrapped := &fakeMatcher{matchFunc, ""}
	matcher := Not(wrapped)

	matcher.Matches(17)
	if suppliedCandidate != 17 {
		t.Errorf("Expected 17, got %v", suppliedCandidate)
	}
}

func TestTrueMatchFromWrapped(t *testing.T) {
	matchFunc := func(c interface{}) (MatchResult, string) {
		return MATCH_TRUE, ""
	}

	wrapped := &fakeMatcher{matchFunc, ""}
	matcher := Not(wrapped)

	res, _ := matcher.Matches(0)
	if res != MATCH_FALSE {
		t.Errorf("Expected MATCH_FALSE, got %v", res)
	}
}

func TestFalseMatchFromWrapped(t *testing.T) {
}

func TestUndefinedMatchFromWrapped(t *testing.T) {
}

func TestDescription(t *testing.T) {
}

<commit_msg>TestUndefinedMatchFromWrapped<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"testing"
)

////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////

type fakeMatcher struct {
	matchFunc func(interface{}) (MatchResult, string)
	description string
}

func (m *fakeMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.matchFunc(c)
}

func (m *fakeMatcher) Description() string {
	return m.description
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestCallsWrapped(t *testing.T) {
	var suppliedCandidate interface{}
	matchFunc := func(c interface{}) (MatchResult, string) {
		suppliedCandidate = c
		return MATCH_TRUE, ""
	}

	wrapped := &fakeMatcher{matchFunc, ""}
	matcher := Not(wrapped)

	matcher.Matches(17)
	if suppliedCandidate != 17 {
		t.Errorf("Expected 17, got %v", suppliedCandidate)
	}
}

func TestTrueMatchFromWrapped(t *testing.T) {
	matchFunc := func(c interface{}) (MatchResult, string) {
		return MATCH_TRUE, ""
	}

	wrapped := &fakeMatcher{matchFunc, ""}
	matcher := Not(wrapped)

	res, _ := matcher.Matches(0)
	if res != MATCH_FALSE {
		t.Errorf("Expected MATCH_FALSE, got %v", res)
	}
}

func TestFalseMatchFromWrapped(t *testing.T) {
	matchFunc := func(c interface{}) (MatchResult, string) {
		return MATCH_FALSE, "taco"
	}

	wrapped := &fakeMatcher{matchFunc, ""}
	matcher := Not(wrapped)

	res, err := matcher.Matches(0)
	if res != MATCH_TRUE {
		t.Errorf("Expected MATCH_TRUE, got %v", res)
	}

	if err != "" {
		t.Errorf("Expected empty string, got %v", err)
	}
}

func TestUndefinedMatchFromWrapped(t *testing.T) {
	matchFunc := func(c interface{}) (MatchResult, string) {
		return MATCH_UNDEFINED, "taco"
	}

	wrapped := &fakeMatcher{matchFunc, ""}
	matcher := Not(wrapped)

	res, err := matcher.Matches(0)
	if res != MATCH_UNDEFINED {
		t.Errorf("Expected MATCH_UNDEFINED, got %v", res)
	}

	if err != "taco" {
		t.Errorf("Expected taco, got %v", err)
	}
}

func TestDescription(t *testing.T) {
}

<|endoftext|>


<commit_before>// +build ignore

// Simple LED blinker, works OOTB on a RPi. However, it does not clean up
// after itself. So might leave the LED On. The RPi is not harmed though.

package main

import (
	"time"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
)

func main() {
	for {
		embd.LEDToggle(0)
		time.Sleep(250 * time.Millisecond)
	}
}
<commit_msg>samples: change simpleblinker to match docs<commit_after>// +build ignore

// Simple LED blinker, works OOTB on a RPi. However, it does not clean up
// after itself. So might leave the LED On. The RPi is not harmed though.

package main

import (
	"time"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
)

func main() {
	for {
		embd.LEDToggle("LED0")
		time.Sleep(250 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package diffscanner

import (
	"cred-alert/scanners"
	"regexp"
	"strconv"
	"strings"

	"code.cloudfoundry.org/lager"
)

var fileHeaderPattern = regexp.MustCompile(`^\+\+\+\s\w\/(.*)$`)
var contextAddedLinePattern = regexp.MustCompile(`^(?:\s|\+)`)
var plusMinusSpacePattern = regexp.MustCompile(`^(?:\s|\+|\-)(.*)`)
var hunkHeaderRegexp = regexp.MustCompile(`^@@.*\+(\d+),?\d+?\s@@`)

type DiffScanner struct {
	diff              []string
	cursor            int
	currentPath       string
	currentLineNumber int
}

func NewDiffScanner(diff string) *DiffScanner {
	return &DiffScanner{
		cursor: -1,
		diff:   strings.Split(diff, "\n"),
	}
}

func (d *DiffScanner) Scan(logger lager.Logger) bool {
	logger = logger.Session("diff-scanner").Session("scan")
	logger.Debug("starting")
	defer logger.Debug("done")

	for {
		d.cursor++
		if d.cursor >= len(d.diff) {
			break
		}

		rawLine := d.diff[d.cursor]

		matches := fileHeaderPattern.FindStringSubmatch(rawLine)
		if len(matches) == 2 {
			d.currentPath = matches[1]
			continue
		}

		matches = hunkHeaderRegexp.FindStringSubmatch(rawLine)
		if len(matches) == 2 {
			startLine, err := strconv.Atoi(matches[1])
			if err != nil {
				logger.Error("failed", err)
				break
			}
			d.currentLineNumber = startLine - 1
			continue
		}

		matches = contextAddedLinePattern.FindStringSubmatch(rawLine)
		if len(matches) == 1 {
			d.currentLineNumber++
			return true
		}
	}

	return false
}

func (d *DiffScanner) Line(logger lager.Logger) *scanners.Line {
	logger = logger.Session("line", lager.Data{
		"line-number": d.currentLineNumber,
		"path":        d.currentPath,
	})
	logger.Debug("starting")
	defer logger.Debug("done")

	var content string
	matches := plusMinusSpacePattern.FindStringSubmatch(d.diff[d.cursor])
	if len(matches) == 2 {
		content = matches[1]
	}

	return &scanners.Line{
		Content:    []byte(content),
		LineNumber: d.currentLineNumber,
		Path:       d.currentPath,
	}
}
<commit_msg>rawLine -> line<commit_after>package diffscanner

import (
	"cred-alert/scanners"
	"regexp"
	"strconv"
	"strings"

	"code.cloudfoundry.org/lager"
)

var fileHeaderPattern = regexp.MustCompile(`^\+\+\+\s\w\/(.*)$`)
var contextAddedLinePattern = regexp.MustCompile(`^(?:\s|\+)`)
var plusMinusSpacePattern = regexp.MustCompile(`^(?:\s|\+|\-)(.*)`)
var hunkHeaderRegexp = regexp.MustCompile(`^@@.*\+(\d+),?\d+?\s@@`)

type DiffScanner struct {
	diff              []string
	cursor            int
	currentPath       string
	currentLineNumber int
}

func NewDiffScanner(diff string) *DiffScanner {
	return &DiffScanner{
		cursor: -1,
		diff:   strings.Split(diff, "\n"),
	}
}

func (d *DiffScanner) Scan(logger lager.Logger) bool {
	logger = logger.Session("diff-scanner").Session("scan")
	logger.Debug("starting")
	defer logger.Debug("done")

	for {
		d.cursor++
		if d.cursor >= len(d.diff) {
			break
		}

		line := d.diff[d.cursor]

		matches := fileHeaderPattern.FindStringSubmatch(line)
		if len(matches) == 2 {
			d.currentPath = matches[1]
			continue
		}

		matches = hunkHeaderRegexp.FindStringSubmatch(line)
		if len(matches) == 2 {
			startLine, err := strconv.Atoi(matches[1])
			if err != nil {
				logger.Error("failed", err)
				break
			}
			d.currentLineNumber = startLine - 1
			continue
		}

		matches = contextAddedLinePattern.FindStringSubmatch(line)
		if len(matches) == 1 {
			d.currentLineNumber++
			return true
		}
	}

	return false
}

func (d *DiffScanner) Line(logger lager.Logger) *scanners.Line {
	logger = logger.Session("line", lager.Data{
		"line-number": d.currentLineNumber,
		"path":        d.currentPath,
	})
	logger.Debug("starting")
	defer logger.Debug("done")

	var content string
	matches := plusMinusSpacePattern.FindStringSubmatch(d.diff[d.cursor])
	if len(matches) == 2 {
		content = matches[1]
	}

	return &scanners.Line{
		Content:    []byte(content),
		LineNumber: d.currentLineNumber,
		Path:       d.currentPath,
	}
}
<|endoftext|>


<commit_before>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"os"

	"github.com/inconshreveable/log15"
	"github.com/pkg/errors"
	"github.com/rbastic/dyndao/object"
	"github.com/rbastic/dyndao/schema"
)

// NOTE: For foreign key filling, we do not check to see if there are conflicts
// with regards to the uniqueness of primary key names.
func pkQueryValsFromKV(obj *object.Object, sch *schema.Schema, parentTableName string) (map[string]interface{}, error) {
	qv := make(map[string]interface{})

	schemaTable := sch.GetTable(parentTableName)
	if schemaTable == nil {
		return nil, fmt.Errorf("pkQueryValsFromKV: no schema table for table named %s", parentTableName)
	}
	schemaPrimary := schemaTable.Primary

	for fName, field := range schemaTable.Fields {
		if field.IsIdentity || field.IsForeignKey || field.Name == schemaPrimary {
			qv[fName] = obj.Get(fName)
		}
	}
	return qv, nil
}

func (o ORM) recurseAndSave(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	rowsAff, err := o.SaveObject(ctx, tx, obj)
	if err != nil {
		return 0, err
	}

	table := o.s.GetTable(obj.Type)
	pkVal := obj.Get(table.Primary)

	for _, v := range obj.Children {
		for _, childObj := range v {
			// set the primary key in the child object, if it exists in the child object's table
			childTable, ok := o.s.Tables[childObj.Type]
			if !ok {
				return 0, fmt.Errorf("recurseAndSave: Unknown child object type %s for parent type %s", childObj.Type, obj.Type)
			}
			// TODO: support propagation of additional primary keys that are
			// saved from previous recursive saves
			// ... check if the child schema table contains
			// the parent's primary key field as a name
			_, ok = childTable.Fields[table.Primary]
			if ok {
				// set in the child object if the table contains the primary
				childObj.Set(table.Primary, pkVal)
			}

			aff, err := o.recurseAndSave(ctx, tx, childObj)
			if err != nil {
				return rowsAff + aff, err
			}
		}
	}
	return rowsAff, err
}

// SaveAllInsideTx will attempt to save an entire nested object structure inside of a single transaction.
func (o ORM) SaveAllInsideTx(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	// TODO: Review this code for how it uses transactions / rollbacks.
	rowsAff, err := o.recurseAndSave(ctx, tx, obj)
	if err != nil {
		err2 := tx.Rollback()
		if err2 != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, err2.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveAll will attempt to save an entire nested object structure inside of a single transaction.
// It begins the transaction, attempts to recursively save the object and all of it's children,
// and any of the children's children, and then will finally rollback/commit as necessary.
func (o ORM) SaveAll(ctx context.Context, obj *object.Object) (int64, error) {
	tx, err := o.RawConn.BeginTx(ctx, nil)
	if err != nil {
		return 0, err
	}
	rowsAff, err := o.SaveAllInsideTx(ctx, tx, obj)
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}

	err = tx.Commit()
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveObjectButErrorIfInsert function will UPDATE a record and error if it
// appears that an INSERT should have been performed. This could be necessary in
// situations where an INSERT would compromise the integrity of the data.  If
// given a transaction, it will use that to attempt to insert the data.
func (o ORM) SaveObjectButErrorIfInsert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: unknown object table " + obj.Type)
	}
	// skip objects that are saved
	if obj.GetSaved() {
		return 0, nil
	}
	// ensure we have a primary key
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty primary key for " + obj.Type)
	}
	// ensure the primary key has a field config
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return 0, fmt.Errorf("SaveObjectButErrorIfInsert: Expected to perform Update on obj: %v", obj)
	}
	return o.Update(ctx, tx, obj)
}

// SaveObject function will INSERT or UPDATE a record. It does not attempt to
// save any of the children. If given a transaction, it will use that to
// attempt to insert the data.
func (o ORM) SaveObject(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObject: unknown object table " + obj.Type)
	}
	// skip if object is saved
	if obj.GetSaved() {
		return 0, nil
	}
	// retrieve primary key value
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObject: empty primary key for " + obj.Type)
	}
	// skip if primary key has no field configuration in table schema
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObject: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return o.Insert(ctx, tx, obj)
	}
	return o.Update(ctx, tx, obj)
}

// use transaction if needed, otherwise just execute a non-transactionalized operation
func stmtFromDbOrTx(ctx context.Context, o ORM, tx *sql.Tx, sqlStr string) (*sql.Stmt, error) {
	var stmt *sql.Stmt
	var err error
	if tx != nil {
		stmt, err = tx.PrepareContext(ctx, sqlStr)
	} else {
		stmt, err = o.RawConn.PrepareContext(ctx, sqlStr)
	}
	return stmt, err
}

// Insert function will INSERT a record, given an optional transaction and an object.
// It returns the number of rows affected (int64) and any error that may have occurred.
func (o ORM) Insert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	objTable := o.s.GetTable(obj.Type)
	if objTable == nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", "thing was unknown")
		}
		return 0, errors.New("Insert: unknown object table " + obj.Type)
	}
	sqlStr, bindArgs, err := o.sqlGen.BindingInsert(o.s, obj.Type, obj.KV)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_INSERT") != "" {
		fmt.Println("Insert/sqlStr=", sqlStr, "bindArgs=", bindArgs)
	}

	// FIXME: Possible bug in rana ora.v4? I wouldn't have expected that I'd
	// have to append a parameter like this, based on reading the code.
	if !o.sqlGen.CallerSuppliesPrimaryKey() {
		if o.sqlGen.FixLastInsertIDbug() {
			var lastID int64
			bindArgs = append(bindArgs, &lastID)
		}
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", err)
		}

		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO: logging implementation
		}
	}()

	res, err := stmt.ExecContext(ctx, bindArgs...)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}

		return 0, errors.Wrap(err, "Insert/ExecContext")
	}

	// If we are not expecting the caller to supply the primary key,
	// then we should not try to capture the last value (for example,
	// using LAST_INSERT_ID() with MySQL..)
	// TODO: Should CallerSuppliesPrimaryKey be per-table?
	if !o.sqlGen.CallerSuppliesPrimaryKey() {
		newID, err := res.LastInsertId()
		if err != nil {
			if os.Getenv("DEBUG_INSERT") != "" {
				fmt.Println("orm/save error", err)
			}
			return 0, err
		}
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("DEBUG Insert received newID=", newID)
		}

		obj.Set(objTable.Primary, newID) // Set the new primary key in the object
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}
		return 0, err
	}
	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any
	return rowsAff, nil
}

// Update function will UPDATE a record ...
func (o ORM) Update(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	sqlStr, bindArgs, bindWhere, err := o.sqlGen.BindingUpdate(o.s, obj)
	if err != nil {
		if os.Getenv("DEBUG_UPDATE") != "" {
			fmt.Println("Update/sqlStr, err=", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_UPDATE") != "" {
		fmt.Println("Update/sqlStr=", sqlStr, "bindArgs=", bindArgs, "bindWhere=", bindWhere)
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO logging implementation
		}
	}()

	allBind := append(bindArgs, bindWhere...)
	res, err := stmt.ExecContext(ctx, allBind...)
	if err != nil {
		return 0, errors.Wrap(err, "Update")
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}

	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any

	return rowsAff, nil
}
<commit_msg>Add arg dereferencing to Inserts and Updates to handle lack of support in goracle driver<commit_after>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"reflect"

	"github.com/inconshreveable/log15"
	"github.com/pkg/errors"
	"github.com/rbastic/dyndao/object"
	"github.com/rbastic/dyndao/schema"
)

// NOTE: For foreign key filling, we do not check to see if there are conflicts
// with regards to the uniqueness of primary key names.
func pkQueryValsFromKV(obj *object.Object, sch *schema.Schema, parentTableName string) (map[string]interface{}, error) {
	qv := make(map[string]interface{})

	schemaTable := sch.GetTable(parentTableName)
	if schemaTable == nil {
		return nil, fmt.Errorf("pkQueryValsFromKV: no schema table for table named %s", parentTableName)
	}
	schemaPrimary := schemaTable.Primary

	for fName, field := range schemaTable.Fields {
		if field.IsIdentity || field.IsForeignKey || field.Name == schemaPrimary {
			qv[fName] = obj.Get(fName)
		}
	}
	return qv, nil
}

func (o ORM) recurseAndSave(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	rowsAff, err := o.SaveObject(ctx, tx, obj)
	if err != nil {
		return 0, err
	}

	table := o.s.GetTable(obj.Type)
	pkVal := obj.Get(table.Primary)

	for _, v := range obj.Children {
		for _, childObj := range v {
			// set the primary key in the child object, if it exists in the child object's table
			childTable, ok := o.s.Tables[childObj.Type]
			if !ok {
				return 0, fmt.Errorf("recurseAndSave: Unknown child object type %s for parent type %s", childObj.Type, obj.Type)
			}
			// TODO: support propagation of additional primary keys that are
			// saved from previous recursive saves
			// ... check if the child schema table contains
			// the parent's primary key field as a name
			_, ok = childTable.Fields[table.Primary]
			if ok {
				// set in the child object if the table contains the primary
				childObj.Set(table.Primary, pkVal)
			}

			aff, err := o.recurseAndSave(ctx, tx, childObj)
			if err != nil {
				return rowsAff + aff, err
			}
		}
	}
	return rowsAff, err
}

// SaveAllInsideTx will attempt to save an entire nested object structure inside of a single transaction.
func (o ORM) SaveAllInsideTx(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	// TODO: Review this code for how it uses transactions / rollbacks.
	rowsAff, err := o.recurseAndSave(ctx, tx, obj)
	if err != nil {
		err2 := tx.Rollback()
		if err2 != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, err2.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveAll will attempt to save an entire nested object structure inside of a single transaction.
// It begins the transaction, attempts to recursively save the object and all of it's children,
// and any of the children's children, and then will finally rollback/commit as necessary.
func (o ORM) SaveAll(ctx context.Context, obj *object.Object) (int64, error) {
	tx, err := o.RawConn.BeginTx(ctx, nil)
	if err != nil {
		return 0, err
	}
	rowsAff, err := o.SaveAllInsideTx(ctx, tx, obj)
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}

	err = tx.Commit()
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveObjectButErrorIfInsert function will UPDATE a record and error if it
// appears that an INSERT should have been performed. This could be necessary in
// situations where an INSERT would compromise the integrity of the data.  If
// given a transaction, it will use that to attempt to insert the data.
func (o ORM) SaveObjectButErrorIfInsert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: unknown object table " + obj.Type)
	}
	// skip objects that are saved
	if obj.GetSaved() {
		return 0, nil
	}
	// ensure we have a primary key
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty primary key for " + obj.Type)
	}
	// ensure the primary key has a field config
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return 0, fmt.Errorf("SaveObjectButErrorIfInsert: Expected to perform Update on obj: %v", obj)
	}
	return o.Update(ctx, tx, obj)
}

// SaveObject function will INSERT or UPDATE a record. It does not attempt to
// save any of the children. If given a transaction, it will use that to
// attempt to insert the data.
func (o ORM) SaveObject(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObject: unknown object table " + obj.Type)
	}
	// skip if object is saved
	if obj.GetSaved() {
		return 0, nil
	}
	// retrieve primary key value
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObject: empty primary key for " + obj.Type)
	}
	// skip if primary key has no field configuration in table schema
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObject: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return o.Insert(ctx, tx, obj)
	}
	return o.Update(ctx, tx, obj)
}

// use transaction if needed, otherwise just execute a non-transactionalized operation
func stmtFromDbOrTx(ctx context.Context, o ORM, tx *sql.Tx, sqlStr string) (*sql.Stmt, error) {
	var stmt *sql.Stmt
	var err error
	if tx != nil {
		stmt, err = tx.PrepareContext(ctx, sqlStr)
	} else {
		stmt, err = o.RawConn.PrepareContext(ctx, sqlStr)
	}
	return stmt, err
}

func maybeDereferenceArgs(arg interface{}) interface{} {
	v := reflect.ValueOf(arg)
	return reflect.Indirect(v).Interface()
}

// Insert function will INSERT a record, given an optional transaction and an object.
// It returns the number of rows affected (int64) and any error that may have occurred.
func (o ORM) Insert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	objTable := o.s.GetTable(obj.Type)
	if objTable == nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", "thing was unknown")
		}
		return 0, errors.New("Insert: unknown object table " + obj.Type)
	}
	sqlStr, bindArgs, err := o.sqlGen.BindingInsert(o.s, obj.Type, obj.KV)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_INSERT") != "" {
		fmt.Println("Insert/sqlStr=", sqlStr, "bindArgs=", bindArgs)
	}

	// FIXME: Possible bug in rana ora.v4? I wouldn't have expected that I'd
	// have to append a parameter like this, based on reading the code.
	if !o.sqlGen.CallerSuppliesPrimaryKey() {
		if o.sqlGen.FixLastInsertIDbug() {
			var lastID int64
			bindArgs = append(bindArgs, &lastID)
		}
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", err)
		}

		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO: logging implementation
		}
	}()

	newBindArgs := make([]interface{}, len(bindArgs))
	for i, arg := range bindArgs {
		newBindArgs[i] = maybeDereferenceArgs(arg)
	}

	res, err := stmt.ExecContext(ctx, newBindArgs...)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}

		return 0, errors.Wrap(err, "Insert/ExecContext")
	}

	// If we are not expecting the caller to supply the primary key,
	// then we should not try to capture the last value (for example,
	// using LAST_INSERT_ID() with MySQL..)
	// TODO: Should CallerSuppliesPrimaryKey be per-table?
	if !o.sqlGen.CallerSuppliesPrimaryKey() {
		newID, err := res.LastInsertId()
		if err != nil {
			if os.Getenv("DEBUG_INSERT") != "" {
				fmt.Println("orm/save error", err)
			}
			return 0, err
		}
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("DEBUG Insert received newID=", newID)
		}

		obj.Set(objTable.Primary, newID) // Set the new primary key in the object
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}
		return 0, err
	}
	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any
	return rowsAff, nil
}

// Update function will UPDATE a record ...
func (o ORM) Update(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	sqlStr, bindArgs, bindWhere, err := o.sqlGen.BindingUpdate(o.s, obj)
	if err != nil {
		if os.Getenv("DEBUG_UPDATE") != "" {
			fmt.Println("Update/sqlStr, err=", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_UPDATE") != "" {
		fmt.Println("Update/sqlStr=", sqlStr, "bindArgs=", bindArgs, "bindWhere=", bindWhere)
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO logging implementation
		}
	}()

	allBind := append(bindArgs, bindWhere...)
	newAllBind := make([]interface{}, len(allBind))
	for i, arg := range allBind {
		newAllBind[i] = maybeDereferenceArgs(arg)
	}
	res, err := stmt.ExecContext(ctx, newAllBind...)
	if err != nil {
		return 0, errors.Wrap(err, "Update")
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}

	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any

	return rowsAff, nil
}
<|endoftext|>