<commit_before>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 xsrfangular

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/plugins/xsrf"
)

// Interceptor provides protection against Cross-Site Request Forgery attacks
// for Angular's XHR requests.
//
// See https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection for more details.
type Interceptor struct {
	// TokenCookieName is the name of the seesion cookie that holds the XSRF
	// token.
	TokenCookieName string
	// TokenHeaderName is the name of the HTTP header that holds the XSRF token.
	TokenHeaderName string
}

var _ safehttp.Interceptor = &Interceptor{}

// Default creates an Interceptor with TokenCookieName set to XSRF-TOKEN and
// TokenHeaderName set to X-XSRF-TOKEN, their default values. However, in order
// to prevent collisions when multiple applications share the same domain or
// subdomain, each application should set a unique name for the cookie.
func Default() *Interceptor {
	return &Interceptor{
		TokenCookieName: "XSRF-TOKEN",
		TokenHeaderName: "X-XSRF-TOKEN",
	}
}

// Before checks for the presence of a matching XSRF token, generated on the
// first page access, in both a cookie and a header. Their names should be set
// when the Interceptor is created.
func (it *Interceptor) Before(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest, _ safehttp.InterceptorConfig) safehttp.Result {
	if xsrf.StatePreserving(r) {
		return safehttp.NotWritten()
	}

	c, err := r.Cookie(it.TokenCookieName)
	if err != nil || c.Value() == "" {
		return w.WriteError(safehttp.StatusForbidden)
	}

	tok := r.Header.Get(it.TokenHeaderName)
	if tok == "" || tok != c.Value() {
		// Only JavaScript running on the user domain can read the
		// cookie and correctly set the token header. Hence, if the same token
		// is found in both the cookie and the header, this guarantees the
		// request came from the user's domain.
		return w.WriteError(safehttp.StatusUnauthorized)
	}

	return safehttp.NotWritten()
}

func (it *Interceptor) addTokenCookie(w *safehttp.ResponseWriter) error {
	tok := make([]byte, 20)
	if _, err := rand.Read(tok); err != nil {
		return fmt.Errorf("crypto/rand.Read: %v", err)
	}
	c := safehttp.NewCookie(it.TokenCookieName, base64.StdEncoding.EncodeToString(tok))

	c.SetSameSite(safehttp.SameSiteStrictMode)
	c.SetPath("/")
	// Set the duration of the token cookie to 24 hours.
	c.SetMaxAge(86400)
	// Needed in order to make the cookie accessible by JavaScript
	// running on the user's domain.
	c.DisableHTTPOnly()

	if err := w.SetCookie(c); err != nil {
		return err
	}
	return nil
}

// Commit generates a cryptographically secure random cookie on the first state
// preserving request (GET, HEAD or OPTION) and sets it in the response. On
// every subsequent request the cookie is expected alongside a header that
// matches its value.
func (it *Interceptor) Commit(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest, resp safehttp.Response, _ safehttp.InterceptorConfig) safehttp.Result {
	if c, err := r.Cookie(it.TokenCookieName); err == nil && c.Value() != "" {
		return safehttp.NotWritten()
	}

	if !xsrf.StatePreserving(r) {
		return w.WriteError(safehttp.StatusForbidden)
	}

	err := it.addTokenCookie(w)
	if err != nil {
		// A 500 error is returned when the plugin fails to set the Set-Cookie
		// header in the response writer as this is a server misconfiguration.
		return w.WriteError(safehttp.StatusInternalServerError)
	}
	return safehttp.NotWritten()
}

// OnError is a no-op, required to satisfy the safehttp.Interceptor interface.
func (it *Interceptor) OnError(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest, resp safehttp.Response, _ safehttp.InterceptorConfig) safehttp.Result {
	return safehttp.NotWritten()
}
<commit_msg>Use the time package to specify the timeout for the Angular token cookie<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 xsrfangular

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/plugins/xsrf"
	"strconv"
	"time"
)

// Interceptor provides protection against Cross-Site Request Forgery attacks
// for Angular's XHR requests.
//
// See https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection for more details.
type Interceptor struct {
	// TokenCookieName is the name of the seesion cookie that holds the XSRF
	// token.
	TokenCookieName string
	// TokenHeaderName is the name of the HTTP header that holds the XSRF token.
	TokenHeaderName string
}

var _ safehttp.Interceptor = &Interceptor{}

// Default creates an Interceptor with TokenCookieName set to XSRF-TOKEN and
// TokenHeaderName set to X-XSRF-TOKEN, their default values. However, in order
// to prevent collisions when multiple applications share the same domain or
// subdomain, each application should set a unique name for the cookie.
//
// See https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection for more details.
func Default() *Interceptor {
	return &Interceptor{
		TokenCookieName: "XSRF-TOKEN",
		TokenHeaderName: "X-XSRF-TOKEN",
	}
}

// Before checks for the presence of a matching XSRF token, generated on the
// first page access, in both a cookie and a header. Their names should be set
// when the Interceptor is created.
func (it *Interceptor) Before(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest, _ safehttp.InterceptorConfig) safehttp.Result {
	if xsrf.StatePreserving(r) {
		return safehttp.NotWritten()
	}

	c, err := r.Cookie(it.TokenCookieName)
	if err != nil || c.Value() == "" {
		return w.WriteError(safehttp.StatusForbidden)
	}

	tok := r.Header.Get(it.TokenHeaderName)
	if tok == "" || tok != c.Value() {
		// Only JavaScript running on the user domain can read the
		// cookie and correctly set the token header. Hence, if the same token
		// is found in both the cookie and the header, this guarantees the
		// request came from the user's domain.
		return w.WriteError(safehttp.StatusUnauthorized)
	}

	return safehttp.NotWritten()
}

func (it *Interceptor) addTokenCookie(w *safehttp.ResponseWriter) error {
	tok := make([]byte, 20)
	if _, err := rand.Read(tok); err != nil {
		return fmt.Errorf("crypto/rand.Read: %v", err)
	}
	c := safehttp.NewCookie(it.TokenCookieName, base64.StdEncoding.EncodeToString(tok))

	c.SetSameSite(safehttp.SameSiteStrictMode)
	c.SetPath("/")
	d := 24 * time.Hour
	timeout, err := strconv.Atoi(fmt.Sprintf("%.0f", d.Seconds()))
	if err != nil {
		return err
	}
	// Set the duration of the token cookie to 24 hours.
	c.SetMaxAge(timeout)
	// Needed in order to make the cookie accessible by JavaScript
	// running on the user's domain.
	c.DisableHTTPOnly()

	if err := w.SetCookie(c); err != nil {
		return err
	}
	return nil
}

// Commit generates a cryptographically secure random cookie on the first state
// preserving request (GET, HEAD or OPTION) and sets it in the response. On
// every subsequent request the cookie is expected alongside a header that
// matches its value.
func (it *Interceptor) Commit(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest, resp safehttp.Response, _ safehttp.InterceptorConfig) safehttp.Result {
	if c, err := r.Cookie(it.TokenCookieName); err == nil && c.Value() != "" {
		return safehttp.NotWritten()
	}

	if !xsrf.StatePreserving(r) {
		return w.WriteError(safehttp.StatusForbidden)
	}

	err := it.addTokenCookie(w)
	if err != nil {
		// A 500 error is returned when the plugin fails to set the Set-Cookie
		// header in the response writer as this is a server misconfiguration.
		return w.WriteError(safehttp.StatusInternalServerError)
	}
	return safehttp.NotWritten()
}

// OnError is a no-op, required to satisfy the safehttp.Interceptor interface.
func (it *Interceptor) OnError(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest, resp safehttp.Response, _ safehttp.InterceptorConfig) safehttp.Result {
	return safehttp.NotWritten()
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package featuretests

import (
	"github.com/juju/cmd"
	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	cmdmetadata "github.com/juju/juju/cmd/plugins/juju-metadata"
	jujutesting "github.com/juju/juju/juju/testing"
	"github.com/juju/juju/state/cloudimagemetadata"
	"github.com/juju/juju/testing"
)

type CmdImageMetadataSuite struct {
	jujutesting.RepoSuite
}

func (s *CmdImageMetadataSuite) SetUpTest(c *gc.C) {
	s.RepoSuite.SetUpTest(c)
}

func (s *CmdImageMetadataSuite) run(c *gc.C, args ...string) *cmd.Context {
	command := cmdmetadata.NewSuperCommand()
	context, err := testing.RunCommand(c, command, args...)
	c.Assert(err, jc.ErrorIsNil)
	return context
}

func (s *CmdImageMetadataSuite) TestAddImageCmdStack(c *gc.C) {
	s.assertNoImageMetadata(c)

	s.run(c, "add-image",
		"im-33333",
		"--series", "trusty",
		"--arch", "arch",
		"--stream", "released",
	)

	after, err := s.State.CloudImageMetadataStorage.FindMetadata(cloudimagemetadata.MetadataFilter{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(after, gc.DeepEquals, map[string][]cloudimagemetadata.Metadata{
		"custom": []cloudimagemetadata.Metadata{{
			MetadataAttributes: cloudimagemetadata.MetadataAttributes{
				Stream:  "released",
				Version: "14.04",
				Series:  "trusty",
				Arch:    "arch",
				Source:  "custom"},
			ImageId: "im-33333"}},
	})
}

func (s *CmdImageMetadataSuite) TestListImageCmdOk(c *gc.C) {
	attrs := cloudimagemetadata.MetadataAttributes{
		Stream:          "stream",
		Region:          "region",
		Version:         "14.04",
		Series:          "trusty",
		Arch:            "arch",
		VirtType:        "virtType",
		Source:          "source",
		RootStorageType: "rootStorageType"}
	m := cloudimagemetadata.Metadata{attrs, 0, "1"}
	err := s.State.CloudImageMetadataStorage.SaveMetadata(m)
	c.Assert(err, jc.ErrorIsNil)

	context := s.run(c, "list-images")

	obtainedErr := testing.Stderr(context)
	c.Assert(obtainedErr, gc.Matches, "")

	obtainedValid := testing.Stdout(context)
	c.Assert(obtainedValid, gc.Matches, `
SOURCE  SERIES  ARCH  REGION  IMAGE-ID  STREAM  VIRT-TYPE  STORAGE-TYPE
source  trusty  arch  region  1         stream  virtType   rootStorageType

`[1:])
}

func (s *CmdImageMetadataSuite) TestDeleteImageCmdOk(c *gc.C) {
	attrs := cloudimagemetadata.MetadataAttributes{
		Stream:          "stream",
		Region:          "region",
		Version:         "14.04",
		Series:          "trusty",
		Arch:            "arch",
		VirtType:        "virtType",
		Source:          "source",
		RootStorageType: "rootStorageType"}
	m := cloudimagemetadata.Metadata{attrs, 0, "1"}
	err := s.State.CloudImageMetadataStorage.SaveMetadata(m)
	c.Assert(err, jc.ErrorIsNil)

	context := s.run(c, "delete-image", "1")

	obtainedErr := testing.Stderr(context)
	c.Assert(obtainedErr, gc.Matches, "")

	obtainedValid := testing.Stdout(context)
	c.Assert(obtainedValid, gc.Matches, "")

	s.assertNoImageMetadata(c)
}

func (s *CmdImageMetadataSuite) assertNoImageMetadata(c *gc.C) {
	before, err := s.State.CloudImageMetadataStorage.FindMetadata(cloudimagemetadata.MetadataFilter{})
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
	c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found")
	c.Assert(before, gc.HasLen, 0)
}
<commit_msg>Rebased and resolved conflicts.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package featuretests

import (
	"github.com/juju/cmd"
	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	cmdmetadata "github.com/juju/juju/cmd/plugins/juju-metadata"
	jujutesting "github.com/juju/juju/juju/testing"
	"github.com/juju/juju/state/cloudimagemetadata"
	"github.com/juju/juju/testing"
)

type CmdImageMetadataSuite struct {
	jujutesting.RepoSuite
}

func (s *CmdImageMetadataSuite) SetUpTest(c *gc.C) {
	s.RepoSuite.SetUpTest(c)
}

func (s *CmdImageMetadataSuite) run(c *gc.C, args ...string) *cmd.Context {
	command := cmdmetadata.NewSuperCommand()
	context, err := testing.RunCommand(c, command, args...)
	c.Assert(err, jc.ErrorIsNil)
	return context
}

func (s *CmdImageMetadataSuite) TestAddImageCmdStack(c *gc.C) {
	s.assertNoImageMetadata(c)

	s.run(c, "add-image",
		"im-33333",
		"--series", "trusty",
		"--arch", "arch",
		"--stream", "released",
	)

	after, err := s.State.CloudImageMetadataStorage.FindMetadata(cloudimagemetadata.MetadataFilter{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(after, gc.DeepEquals, map[string][]cloudimagemetadata.Metadata{
		"custom": []cloudimagemetadata.Metadata{{
			MetadataAttributes: cloudimagemetadata.MetadataAttributes{
				Stream:  "released",
				Version: "14.04",
				Series:  "trusty",
				Arch:    "arch",
				Source:  "custom"},
			ImageId: "im-33333"}},
	})
}

func (s *CmdImageMetadataSuite) TestListImageCmdOk(c *gc.C) {
	attrs := cloudimagemetadata.MetadataAttributes{
		Stream:          "stream",
		Region:          "region",
		Version:         "14.04",
		Series:          "trusty",
		Arch:            "arch",
		VirtType:        "virtType",
		Source:          "source",
		RootStorageType: "rootStorageType"}
	m := cloudimagemetadata.Metadata{attrs, 0, "1"}
	err := s.State.CloudImageMetadataStorage.SaveMetadata([]cloudimagemetadata.Metadata{m})
	c.Assert(err, jc.ErrorIsNil)

	context := s.run(c, "list-images")

	obtainedErr := testing.Stderr(context)
	c.Assert(obtainedErr, gc.Matches, "")

	obtainedValid := testing.Stdout(context)
	c.Assert(obtainedValid, gc.Matches, `
SOURCE  SERIES  ARCH  REGION  IMAGE-ID  STREAM  VIRT-TYPE  STORAGE-TYPE
source  trusty  arch  region  1         stream  virtType   rootStorageType

`[1:])
}

func (s *CmdImageMetadataSuite) TestDeleteImageCmdOk(c *gc.C) {
	attrs := cloudimagemetadata.MetadataAttributes{
		Stream:          "stream",
		Region:          "region",
		Version:         "14.04",
		Series:          "trusty",
		Arch:            "arch",
		VirtType:        "virtType",
		Source:          "source",
		RootStorageType: "rootStorageType"}
	m := cloudimagemetadata.Metadata{attrs, 0, "1"}
	err := s.State.CloudImageMetadataStorage.SaveMetadata([]cloudimagemetadata.Metadata{m})
	c.Assert(err, jc.ErrorIsNil)

	context := s.run(c, "delete-image", "1")

	obtainedErr := testing.Stderr(context)
	c.Assert(obtainedErr, gc.Matches, "")

	obtainedValid := testing.Stdout(context)
	c.Assert(obtainedValid, gc.Matches, "")

	s.assertNoImageMetadata(c)
}

func (s *CmdImageMetadataSuite) assertNoImageMetadata(c *gc.C) {
	before, err := s.State.CloudImageMetadataStorage.FindMetadata(cloudimagemetadata.MetadataFilter{})
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
	c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found")
	c.Assert(before, gc.HasLen, 0)
}
<|endoftext|>


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

package jsonpath

import (
	"github.com/oliveagle/jsonpath"
	"encoding/json"
)

type Parser struct {
	data interface{}
}

func NewParser(b []byte) *Parser {
	p := Parser{}
	json.Unmarshal(b, &p.data)
	return &p
}

func (p Parser) Value(path string) (interface{}, error) {
	comp, err := jsonpath.Compile(path)
	if err != nil {
		return nil, err
	}
	return comp.Lookup(p.data)
}

func (p Parser) StringValue(path string) (string, error) {
	res, err := p.Value(path)
	return res.(string), err
}
<commit_msg>Return empty string on missing jsonpath<commit_after>/*
 * Copyright 2018 the original author or authors.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package jsonpath

import (
	"github.com/oliveagle/jsonpath"
	"encoding/json"
)

type Parser struct {
	data interface{}
}

func NewParser(b []byte) *Parser {
	p := Parser{}
	json.Unmarshal(b, &p.data)
	return &p
}

func (p Parser) Value(path string) (interface{}, error) {
	comp, err := jsonpath.Compile(path)
	if err != nil {
		return nil, err
	}
	return comp.Lookup(p.data)
}

func (p Parser) StringValue(path string) (string, error) {
	res, err := p.Value(path)
	if res == nil {
		return "", err
	}
	return res.(string), err
}
<|endoftext|>


<commit_before>// Copyright 2015 go-swagger maintainers
//
// 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 validation

import (
	"reflect"
	"strings"

	"github.com/go-openapi/errors"
	"github.com/go-openapi/spec"
	"github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"
)

type typeValidator struct {
	Type   spec.StringOrArray
	Format string
	In     string
	Path   string
}

var jsonTypeNames = map[string]struct{}{
	"array":   struct{}{},
	"boolean": struct{}{},
	"integer": struct{}{},
	"null":    struct{}{},
	"number":  struct{}{},
	"object":  struct{}{},
	"string":  struct{}{},
}

func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
	switch data.(type) {
	case []byte:
		return "string", "byte"
	case strfmt.Date, *strfmt.Date:
		return "string", "date"
	case strfmt.DateTime, *strfmt.DateTime:
		return "string", "datetime"
	// case httpkit.File, *httpkit.File:
	// 	return "file", ""
	case strfmt.URI, *strfmt.URI:
		return "string", "uri"
	case strfmt.Email, *strfmt.Email:
		return "string", "email"
	case strfmt.Hostname, *strfmt.Hostname:
		return "string", "hostname"
	case strfmt.IPv4, *strfmt.IPv4:
		return "string", "ipv4"
	case strfmt.IPv6, *strfmt.IPv6:
		return "string", "ipv6"
	case strfmt.UUID, *strfmt.UUID:
		return "string", "uuid"
	case strfmt.UUID3, *strfmt.UUID3:
		return "string", "uuid3"
	case strfmt.UUID4, *strfmt.UUID4:
		return "string", "uuid4"
	case strfmt.UUID5, *strfmt.UUID5:
		return "string", "uuid5"
	case strfmt.ISBN, *strfmt.ISBN:
		return "string", "isbn"
	case strfmt.ISBN10, *strfmt.ISBN10:
		return "string", "isbn10"
	case strfmt.ISBN13, *strfmt.ISBN13:
		return "string", "isbn13"
	case strfmt.CreditCard, *strfmt.CreditCard:
		return "string", "creditcard"
	case strfmt.SSN, *strfmt.SSN:
		return "string", "ssn"
	case strfmt.HexColor, *strfmt.HexColor:
		return "string", "hexcolor"
	case strfmt.RGBColor, *strfmt.RGBColor:
		return "string", "rgbcolor"
	default:
		val := reflect.ValueOf(data)
		tpe := val.Type()
		switch tpe.Kind() {
		case reflect.Bool:
			return "boolean", ""
		case reflect.String:
			return "string", ""
		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
			return "integer", "int32"
		case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64:
			return "integer", "int64"
		case reflect.Float32:
			return "number", "float32"
		case reflect.Float64:
			return "number", "float64"
		case reflect.Slice:
			return "array", ""
		case reflect.Map, reflect.Struct:
			return "object", ""
		case reflect.Interface:
			// What to do here?
			panic("dunno what to do here")
		case reflect.Ptr:
			return t.schemaInfoForType(reflect.Indirect(val).Interface())
		}
	}
	return "", ""
}

func (t *typeValidator) SetPath(path string) {
	t.Path = path
}

func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool {
	stpe := reflect.TypeOf(source)
	r := (len(t.Type) > 0 || t.Format != "") && (stpe == specSchemaType || stpe == specParameterType || stpe == specHeaderType)
	//fmt.Printf("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind)
	return r
}

func (t *typeValidator) Validate(data interface{}) *Result {
	result := new(Result)
	result.Inc()
	if data == nil || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(data)), reflect.ValueOf(data)) {
		if len(t.Type) > 0 && !t.Type.Contains("null") { // TODO: if a property is not required it also passes this
			return sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), "null"))
		}
		return result
	}

	// check if the type matches, should be used in every validator chain as first item
	val := reflect.Indirect(reflect.ValueOf(data))
	kind := val.Kind()

	schType, format := t.schemaInfoForType(data)
	//fmt.Println("path:", t.Path, "schType:", schType, "format:", format, "expType:", t.Type, "expFmt:", t.Format, "kind:", val.Kind().String())
	isLowerInt := t.Format == "int64" && format == "int32"
	isLowerFloat := t.Format == "float64" && format == "float32"
	isFloatInt := schType == "number" && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains("integer")
	isIntFloat := schType == "integer" && t.Type.Contains("number")

	if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) {
		return sErr(errors.InvalidType(t.Path, t.In, t.Format, format))
	}
	if !(t.Type.Contains("number") || t.Type.Contains("integer")) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
		return result
	}

	if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) {
		return sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType))
	}
	return result
}
<commit_msg>re-enable file validation<commit_after>// Copyright 2015 go-swagger maintainers
//
// 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 validation

import (
	"reflect"
	"strings"

	"github.com/go-openapi/errors"
	"github.com/go-openapi/runtime"
	"github.com/go-openapi/spec"
	"github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"
)

type typeValidator struct {
	Type   spec.StringOrArray
	Format string
	In     string
	Path   string
}

var jsonTypeNames = map[string]struct{}{
	"array":   struct{}{},
	"boolean": struct{}{},
	"integer": struct{}{},
	"null":    struct{}{},
	"number":  struct{}{},
	"object":  struct{}{},
	"string":  struct{}{},
}

func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
	switch data.(type) {
	case []byte:
		return "string", "byte"
	case strfmt.Date, *strfmt.Date:
		return "string", "date"
	case strfmt.DateTime, *strfmt.DateTime:
		return "string", "datetime"
	case runtime.File, *runtime.File:
		return "file", ""
	case strfmt.URI, *strfmt.URI:
		return "string", "uri"
	case strfmt.Email, *strfmt.Email:
		return "string", "email"
	case strfmt.Hostname, *strfmt.Hostname:
		return "string", "hostname"
	case strfmt.IPv4, *strfmt.IPv4:
		return "string", "ipv4"
	case strfmt.IPv6, *strfmt.IPv6:
		return "string", "ipv6"
	case strfmt.UUID, *strfmt.UUID:
		return "string", "uuid"
	case strfmt.UUID3, *strfmt.UUID3:
		return "string", "uuid3"
	case strfmt.UUID4, *strfmt.UUID4:
		return "string", "uuid4"
	case strfmt.UUID5, *strfmt.UUID5:
		return "string", "uuid5"
	case strfmt.ISBN, *strfmt.ISBN:
		return "string", "isbn"
	case strfmt.ISBN10, *strfmt.ISBN10:
		return "string", "isbn10"
	case strfmt.ISBN13, *strfmt.ISBN13:
		return "string", "isbn13"
	case strfmt.CreditCard, *strfmt.CreditCard:
		return "string", "creditcard"
	case strfmt.SSN, *strfmt.SSN:
		return "string", "ssn"
	case strfmt.HexColor, *strfmt.HexColor:
		return "string", "hexcolor"
	case strfmt.RGBColor, *strfmt.RGBColor:
		return "string", "rgbcolor"
	default:
		val := reflect.ValueOf(data)
		tpe := val.Type()
		switch tpe.Kind() {
		case reflect.Bool:
			return "boolean", ""
		case reflect.String:
			return "string", ""
		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
			return "integer", "int32"
		case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64:
			return "integer", "int64"
		case reflect.Float32:
			return "number", "float32"
		case reflect.Float64:
			return "number", "float64"
		case reflect.Slice:
			return "array", ""
		case reflect.Map, reflect.Struct:
			return "object", ""
		case reflect.Interface:
			// What to do here?
			panic("dunno what to do here")
		case reflect.Ptr:
			return t.schemaInfoForType(reflect.Indirect(val).Interface())
		}
	}
	return "", ""
}

func (t *typeValidator) SetPath(path string) {
	t.Path = path
}

func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool {
	stpe := reflect.TypeOf(source)
	r := (len(t.Type) > 0 || t.Format != "") && (stpe == specSchemaType || stpe == specParameterType || stpe == specHeaderType)
	//fmt.Printf("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind)
	return r
}

func (t *typeValidator) Validate(data interface{}) *Result {
	result := new(Result)
	result.Inc()
	if data == nil || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(data)), reflect.ValueOf(data)) {
		if len(t.Type) > 0 && !t.Type.Contains("null") { // TODO: if a property is not required it also passes this
			return sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), "null"))
		}
		return result
	}

	// check if the type matches, should be used in every validator chain as first item
	val := reflect.Indirect(reflect.ValueOf(data))
	kind := val.Kind()

	schType, format := t.schemaInfoForType(data)
	//fmt.Println("path:", t.Path, "schType:", schType, "format:", format, "expType:", t.Type, "expFmt:", t.Format, "kind:", val.Kind().String())
	isLowerInt := t.Format == "int64" && format == "int32"
	isLowerFloat := t.Format == "float64" && format == "float32"
	isFloatInt := schType == "number" && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains("integer")
	isIntFloat := schType == "integer" && t.Type.Contains("number")

	if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) {
		return sErr(errors.InvalidType(t.Path, t.In, t.Format, format))
	}
	if !(t.Type.Contains("number") || t.Type.Contains("integer")) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
		return result
	}

	if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) {
		return sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType))
	}
	return result
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bobappleyard/readline"
	"io"
	"log"
	"mig"
	"mig/client"
	"os"
	"strconv"
	"strings"
)

// build version
var version string

func main() {
	var err error
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "FATAL: %v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	// command line options
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var quiet = flag.Bool("q", false, "don't display banners and prompts")
	var showversion = flag.Bool("V", false, "show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(version)
		os.Exit(0)
	}

	// silence extra output
	out := os.Stdout
	if *quiet {
		out.Close()
		out, err = os.Open(os.DevNull)
		if err != nil {
			panic(err)
		}
	}
	defer out.Close()

	fmt.Fprintf(out, "\x1b[32;1m"+banner+"\x1b[0m")

	// append a space after completion
	readline.CompletionAppendChar = 0x20
	// load history
	historyfile := homedir + "/.mig_history"
	fi, err := os.Stat(historyfile)
	if err == nil && fi.Size() > 0 {
		err = readline.LoadHistory(historyfile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to load history from %s\n", historyfile)
		}
	}
	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "console-"+version)
	if err != nil {
		panic(err)
	}
	// print platform status
	err = printStatus(cli)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(out, "\nConnected to %s. Exit with \x1b[32;1mctrl+d\x1b[0m. Type \x1b[32;1mhelp\x1b[0m for help.\n", cli.Conf.API.URL)
	for {
		// completion
		var symbols = []string{"action", "agent", "create", "command", "help", "history",
			"exit", "showcfg", "status", "investigator", "search", "where", "and"}
		readline.Completer = func(query, ctx string) []string {
			var res []string
			for _, sym := range symbols {
				if strings.HasPrefix(sym, query) {
					res = append(res, sym)
				}
			}
			return res
		}

		input, err := readline.String("\x1b[32;1mmig>\x1b[0m ")
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println("error: ", err)
			break
		}
		orders := strings.Split(strings.TrimSpace(input), " ")
		switch orders[0] {
		case "action":
			if len(orders) == 2 {
				err = actionReader(input, cli)
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing action id in 'action <id>'")
			}
		case "agent":
			err = agentReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "create":
			if len(orders) == 2 {
				switch orders[1] {
				case "action":
					var a mig.Action
					err = actionLauncher(a, cli)
				case "investigator":
					err = investigatorCreator(cli)
				default:
					fmt.Printf("unknown order 'create %s'\n", orders[1])
				}
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing order, must be 'create <action|investigator>'")
			}
		case "command":
			err = commandReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "exit":
			fmt.Printf("exit\n")
			goto exit
		case "help":
			fmt.Printf(`The following orders are available:
action <id|new>		enter interactive action mode. if <id> is given, go to reader mode. if "new" is given, enter launcher mode.
agent <id>		enter interactive agent reader mode for agent <id>
create action		enter interactive action creation mode
create investigator	create a new investigator, will prompt for name and public key
command <id>		enter command reader mode for command <id>
exit			leave
help			show this help
history <count>		print last <count> entries in history. count=10 by default.
investigator <id>	enter interactive investigator management mode for investigator <id>
search			perform a search. see "search help" for more information.
showcfg			display running configuration
status			display platform status: connected agents, latest actions, ...
`)
		case "history":
			var count int64 = 10
			if len(orders) > 1 {
				count, err = strconv.ParseInt(orders[1], 10, 64)
				if err != nil {
					log.Println(err)
					break
				}
			}
			for i := readline.HistorySize(); i > 0 && count > 0; i, count = i-1, count-1 {
				fmt.Println(readline.GetHistory(i - 1))
			}
		case "investigator":
			err = investigatorReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "search":
			err = search(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "showcfg":
			fmt.Printf("homedir = %s\n[api]\n    url = %s\n[gpg]\n    home = %s\n    keyid = %s\n",
				cli.Conf.API.URL, cli.Conf.Homedir, cli.Conf.GPG.Home, cli.Conf.GPG.KeyID)
		case "status":
			err = printStatus(cli)
			if err != nil {
				log.Println(err)
			}
		case "":
			break
		default:
			fmt.Printf("Unknown order '%s'\n", orders[0])
		}
		readline.AddHistory(input)
	}
exit:
	fmt.Fprintf(out, footer)
	err = readline.SaveHistory(historyfile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to save history to %s\n", historyfile)
	}
}

func printStatus(cli client.Client) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("printStatus() -> %v", e)
		}
	}()
	st, err := cli.GetAPIResource("dashboard")
	if err != nil {
		panic(err)
	}
	var onlineagt, idleagt []string
	actout := make([]string, 2)
	actout[0] = "Latest Actions:"
	actout[1] = "----    ID      ---- + ----         Name         ---- + -Sent- + ----    Date     ---- + ---- Investigators ----"
	var onlineagents, onlineendpoints, idleagents, idleendpoints, newendpoints, doubleagents, disappearedendpoints, flappingendpoints float64
	for _, item := range st.Collection.Items {
		for _, data := range item.Data {
			switch data.Name {
			case "action":
				idstr, name, datestr, invs, sent, err := actionPrintShort(data.Value)
				if err != nil {
					panic(err)
				}
				str := fmt.Sprintf("%s   %s   %6d   %s   %s", idstr, name, sent, datestr, invs)
				actout = append(actout, str)
			case "online agents":
				onlineagents = data.Value.(float64)
			case "online endpoints":
				onlineendpoints = data.Value.(float64)
			case "idle agents":
				idleagents = data.Value.(float64)
			case "idle endpoints":
				idleendpoints = data.Value.(float64)
			case "new endpoints":
				newendpoints = data.Value.(float64)
			case "endpoints running 2 or more agents":
				doubleagents = data.Value.(float64)
			case "disappeared endpoints":
				disappearedendpoints = data.Value.(float64)
			case "flapping endpoints":
				flappingendpoints = data.Value.(float64)
			case "online agents by version":
				bData, err := json.Marshal(data.Value)
				if err != nil {
					panic(err)
				}
				var sum []mig.AgentsVersionsSum
				err = json.Unmarshal(bData, &sum)
				if err != nil {
					panic(err)
				}
				for _, asum := range sum {
					s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count)
					if asum.Count > 1 {
						s += "s"
					}
					onlineagt = append(onlineagt, s)
				}
			case "idle agents by version":
				bData, err := json.Marshal(data.Value)
				if err != nil {
					panic(err)
				}
				var sum []mig.AgentsVersionsSum
				err = json.Unmarshal(bData, &sum)
				if err != nil {
					panic(err)
				}
				for _, asum := range sum {
					s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count)
					if asum.Count > 1 {
						s += "s"
					}
					idleagt = append(idleagt, s)
				}
			}
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	fmt.Printf("\x1b[31;1m| Agents & Endpoints summary:\n"+
		"\x1b[31;1m|\x1b[0m * %.0f online agents on %.0f endpoints\n"+
		"\x1b[31;1m|\x1b[0m * %.0f idle agents on %.0f endpoints\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints are running 2 or more agents\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints appeared over the last 7 days\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints disappeared over the last 7 days\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints have been flapping\n",
		onlineagents, onlineendpoints, idleagents, idleendpoints, doubleagents, newendpoints,
		disappearedendpoints, flappingendpoints)
	fmt.Println("\x1b[31;1m| Online agents by version:\x1b[0m")
	for _, s := range onlineagt {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
	}
	fmt.Println("\x1b[31;1m| Idle agents by version:\x1b[0m")
	for _, s := range idleagt {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
	}
	fmt.Println("\x1b[31;1m|\x1b[0m")
	for _, s := range actout {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
		if len(actout) < 2 {
			fmt.Println("\x1b[31;1m|\x1b[0m * None")
			break
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	return
}

var banner string = `
## ##                                     _.---._     .---.
# # # /-\ ---||  |    /\         __...---' .---. '---'-.   '.
#   #|   | / ||  |   /--\    .-''__.--' _.'( | )'.  '.  '._ :
#   # \_/ ---| \_ \_/    \ .'__-'_ .--'' ._'---'_.-.  '.   '-'.
     ###                         ~ -._ -._''---. -.    '-._   '.
      # |\ |\    /---------|          ~ -.._ _ _ _ ..-_ '.  '-._''--.._
      # | \| \  / |- |__ | |                       -~ -._  '-.  -. '-._''--.._.--''.
     ###|  \  \/  ---__| | |                            ~ ~-.__     -._  '-.__   '. '.
          #####                                               ~~ ~---...__ _    ._ .' '.
          #      /\  --- /-\ |--|----                                    ~  ~--.....--~
          # ### /--\  | |   ||-\  //
          #####/    \ |  \_/ |  \//__
`

var footer string = `
            .-._   _ _ _ _ _ _ _ _
 .-''-.__.-'Oo  '-' ' ' ' ' ' ' ' '-.
'.___ '    .   .--_'-' '-' '-' _'-' '._
 V: V 'vv-'   '_   '.       .'  _..' '.'.
   '=.____.=_.--'   :_.__.__:_   '.   : :
           (((____.-'        '-.  /   : :
                             (((-'\ .' /
                           _____..'  .'
                          '-._____.-'
              Gators are going back underwater.
`
<commit_msg>[minor] corrected help in mig-console<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bobappleyard/readline"
	"io"
	"log"
	"mig"
	"mig/client"
	"os"
	"strconv"
	"strings"
)

// build version
var version string

func main() {
	var err error
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "FATAL: %v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	// command line options
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var quiet = flag.Bool("q", false, "don't display banners and prompts")
	var showversion = flag.Bool("V", false, "show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(version)
		os.Exit(0)
	}

	// silence extra output
	out := os.Stdout
	if *quiet {
		out.Close()
		out, err = os.Open(os.DevNull)
		if err != nil {
			panic(err)
		}
	}
	defer out.Close()

	fmt.Fprintf(out, "\x1b[32;1m"+banner+"\x1b[0m")

	// append a space after completion
	readline.CompletionAppendChar = 0x20
	// load history
	historyfile := homedir + "/.mig_history"
	fi, err := os.Stat(historyfile)
	if err == nil && fi.Size() > 0 {
		err = readline.LoadHistory(historyfile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to load history from %s\n", historyfile)
		}
	}
	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "console-"+version)
	if err != nil {
		panic(err)
	}
	// print platform status
	err = printStatus(cli)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(out, "\nConnected to %s. Exit with \x1b[32;1mctrl+d\x1b[0m. Type \x1b[32;1mhelp\x1b[0m for help.\n", cli.Conf.API.URL)
	for {
		// completion
		var symbols = []string{"action", "agent", "create", "command", "help", "history",
			"exit", "showcfg", "status", "investigator", "search", "where", "and"}
		readline.Completer = func(query, ctx string) []string {
			var res []string
			for _, sym := range symbols {
				if strings.HasPrefix(sym, query) {
					res = append(res, sym)
				}
			}
			return res
		}

		input, err := readline.String("\x1b[32;1mmig>\x1b[0m ")
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println("error: ", err)
			break
		}
		orders := strings.Split(strings.TrimSpace(input), " ")
		switch orders[0] {
		case "action":
			if len(orders) == 2 {
				err = actionReader(input, cli)
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing action id in 'action <id>'")
			}
		case "agent":
			err = agentReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "create":
			if len(orders) == 2 {
				switch orders[1] {
				case "action":
					var a mig.Action
					err = actionLauncher(a, cli)
				case "investigator":
					err = investigatorCreator(cli)
				default:
					fmt.Printf("unknown order 'create %s'\n", orders[1])
				}
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing order, must be 'create <action|investigator>'")
			}
		case "command":
			err = commandReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "exit":
			fmt.Printf("exit\n")
			goto exit
		case "help":
			fmt.Printf(`The following orders are available:
action <id>		enter interactive action reader mode for action <id>
agent <id>		enter interactive agent reader mode for agent <id>
create action		create a new action
create investigator	create a new investigator, will prompt for name and public key
command <id>		enter command reader mode for command <id>
exit			leave
help			show this help
history <count>		print last <count> entries in history. count=10 by default.
investigator <id>	enter interactive investigator management mode for investigator <id>
search			perform a search. see "search help" for more information.
showcfg			display running configuration
status			display platform status: connected agents, latest actions, ...
`)
		case "history":
			var count int64 = 10
			if len(orders) > 1 {
				count, err = strconv.ParseInt(orders[1], 10, 64)
				if err != nil {
					log.Println(err)
					break
				}
			}
			for i := readline.HistorySize(); i > 0 && count > 0; i, count = i-1, count-1 {
				fmt.Println(readline.GetHistory(i - 1))
			}
		case "investigator":
			err = investigatorReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "search":
			err = search(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "showcfg":
			fmt.Printf("homedir = %s\n[api]\n    url = %s\n[gpg]\n    home = %s\n    keyid = %s\n",
				cli.Conf.API.URL, cli.Conf.Homedir, cli.Conf.GPG.Home, cli.Conf.GPG.KeyID)
		case "status":
			err = printStatus(cli)
			if err != nil {
				log.Println(err)
			}
		case "":
			break
		default:
			fmt.Printf("Unknown order '%s'\n", orders[0])
		}
		readline.AddHistory(input)
	}
exit:
	fmt.Fprintf(out, footer)
	err = readline.SaveHistory(historyfile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to save history to %s\n", historyfile)
	}
}

func printStatus(cli client.Client) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("printStatus() -> %v", e)
		}
	}()
	st, err := cli.GetAPIResource("dashboard")
	if err != nil {
		panic(err)
	}
	var onlineagt, idleagt []string
	actout := make([]string, 2)
	actout[0] = "Latest Actions:"
	actout[1] = "----    ID      ---- + ----         Name         ---- + -Sent- + ----    Date     ---- + ---- Investigators ----"
	var onlineagents, onlineendpoints, idleagents, idleendpoints, newendpoints, doubleagents, disappearedendpoints, flappingendpoints float64
	for _, item := range st.Collection.Items {
		for _, data := range item.Data {
			switch data.Name {
			case "action":
				idstr, name, datestr, invs, sent, err := actionPrintShort(data.Value)
				if err != nil {
					panic(err)
				}
				str := fmt.Sprintf("%s   %s   %6d   %s   %s", idstr, name, sent, datestr, invs)
				actout = append(actout, str)
			case "online agents":
				onlineagents = data.Value.(float64)
			case "online endpoints":
				onlineendpoints = data.Value.(float64)
			case "idle agents":
				idleagents = data.Value.(float64)
			case "idle endpoints":
				idleendpoints = data.Value.(float64)
			case "new endpoints":
				newendpoints = data.Value.(float64)
			case "endpoints running 2 or more agents":
				doubleagents = data.Value.(float64)
			case "disappeared endpoints":
				disappearedendpoints = data.Value.(float64)
			case "flapping endpoints":
				flappingendpoints = data.Value.(float64)
			case "online agents by version":
				bData, err := json.Marshal(data.Value)
				if err != nil {
					panic(err)
				}
				var sum []mig.AgentsVersionsSum
				err = json.Unmarshal(bData, &sum)
				if err != nil {
					panic(err)
				}
				for _, asum := range sum {
					s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count)
					if asum.Count > 1 {
						s += "s"
					}
					onlineagt = append(onlineagt, s)
				}
			case "idle agents by version":
				bData, err := json.Marshal(data.Value)
				if err != nil {
					panic(err)
				}
				var sum []mig.AgentsVersionsSum
				err = json.Unmarshal(bData, &sum)
				if err != nil {
					panic(err)
				}
				for _, asum := range sum {
					s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count)
					if asum.Count > 1 {
						s += "s"
					}
					idleagt = append(idleagt, s)
				}
			}
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	fmt.Printf("\x1b[31;1m| Agents & Endpoints summary:\n"+
		"\x1b[31;1m|\x1b[0m * %.0f online agents on %.0f endpoints\n"+
		"\x1b[31;1m|\x1b[0m * %.0f idle agents on %.0f endpoints\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints are running 2 or more agents\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints appeared over the last 7 days\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints disappeared over the last 7 days\n"+
		"\x1b[31;1m|\x1b[0m * %.0f endpoints have been flapping\n",
		onlineagents, onlineendpoints, idleagents, idleendpoints, doubleagents, newendpoints,
		disappearedendpoints, flappingendpoints)
	fmt.Println("\x1b[31;1m| Online agents by version:\x1b[0m")
	for _, s := range onlineagt {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
	}
	fmt.Println("\x1b[31;1m| Idle agents by version:\x1b[0m")
	for _, s := range idleagt {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
	}
	fmt.Println("\x1b[31;1m|\x1b[0m")
	for _, s := range actout {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
		if len(actout) < 2 {
			fmt.Println("\x1b[31;1m|\x1b[0m * None")
			break
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	return
}

var banner string = `
## ##                                     _.---._     .---.
# # # /-\ ---||  |    /\         __...---' .---. '---'-.   '.
#   #|   | / ||  |   /--\    .-''__.--' _.'( | )'.  '.  '._ :
#   # \_/ ---| \_ \_/    \ .'__-'_ .--'' ._'---'_.-.  '.   '-'.
     ###                         ~ -._ -._''---. -.    '-._   '.
      # |\ |\    /---------|          ~ -.._ _ _ _ ..-_ '.  '-._''--.._
      # | \| \  / |- |__ | |                       -~ -._  '-.  -. '-._''--.._.--''.
     ###|  \  \/  ---__| | |                            ~ ~-.__     -._  '-.__   '. '.
          #####                                               ~~ ~---...__ _    ._ .' '.
          #      /\  --- /-\ |--|----                                    ~  ~--.....--~
          # ### /--\  | |   ||-\  //
          #####/    \ |  \_/ |  \//__
`

var footer string = `
            .-._   _ _ _ _ _ _ _ _
 .-''-.__.-'Oo  '-' ' ' ' ' ' ' ' '-.
'.___ '    .   .--_'-' '-' '-' _'-' '._
 V: V 'vv-'   '_   '.       .'  _..' '.'.
   '=.____.=_.--'   :_.__.__:_   '.   : :
           (((____.-'        '-.  /   : :
                             (((-'\ .' /
                           _____..'  .'
                          '-._____.-'
              Gators are going back underwater.
`
<|endoftext|>


<commit_before>package looli

import (
	"github.com/cssivision/router"
	"net/http"
)

type RouterPrefix struct {
	basePath string
	router   *router.Router
	Handlers []HandlerFunc
}

func (p *RouterPrefix) Use(middleware ...HandlerFunc) {
	if len(middleware) == 0 {
		panic("there must be at least one middleware")
	}
	p.Handlers = middleware
}

func (p *RouterPrefix) Get(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodGet, pattern, handlers...)
}

func (p *RouterPrefix) Post(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodGet, pattern, handlers...)
}

func (p *RouterPrefix) Put(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodPut, pattern, handlers...)
}

func (p *RouterPrefix) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
	finalSize := len(p.Handlers) + len(handlers)
	mergedHandlers := make([]HandlerFunc, finalSize)
	copyHandlers(mergedHandlers, p.Handlers)
	copyHandlers(mergedHandlers[len(p.Handlers):], handlers)
	return mergedHandlers
}

func (p *RouterPrefix) Prefix(basePath string) *RouterPrefix {
	return &RouterPrefix{
		basePath: basePath,
		router:   p.router,
		Handlers: p.Handlers,
	}
}

func (p *RouterPrefix) Handle(method, pattern string, handlers ...HandlerFunc) {
	if len(handlers) == 0 {
		panic("there must be at least one handler")
	}

	handlers = p.combineHandlers(handlers)
	muxHandler := compose(handlers)
	p.router.Handle(method, pattern, muxHandler)
}

func copyHandlers(dst, src []HandlerFunc) {
	for index, val := range src {
		dst[index] = val
	}
}
<commit_msg>feat: process test<commit_after>package looli

import (
	"github.com/cssivision/router"
	"net/http"
)

type RouterPrefix struct {
	basePath string
	router   *router.Router
	Handlers []HandlerFunc
}

func (p *RouterPrefix) Use(middleware ...HandlerFunc) {
	if len(middleware) == 0 {
		panic("there must be at least one middleware")
	}
	p.Handlers = middleware
}

func (p *RouterPrefix) Get(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodGet, pattern, handlers...)
}

func (p *RouterPrefix) Post(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodGet, pattern, handlers...)
}

func (p *RouterPrefix) Put(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodPut, pattern, handlers...)
}

func (p *RouterPrefix) Delete(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodDelete, pattern, handlers...)
}

func (p *RouterPrefix) Head(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodHead, pattern, handlers...)
}

func (p *RouterPrefix) Trace(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodTrace, pattern, handlers...)
}

func (p *RouterPrefix) Options(pattern string, handlers ...HandlerFunc) {
	p.Handle(http.MethodOptions, pattern, handlers...)
}

func (p *RouterPrefix) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
	finalSize := len(p.Handlers) + len(handlers)
	mergedHandlers := make([]HandlerFunc, finalSize)
	copyHandlers(mergedHandlers, p.Handlers)
	copyHandlers(mergedHandlers[len(p.Handlers):], handlers)
	return mergedHandlers
}

func (p *RouterPrefix) Prefix(basePath string) *RouterPrefix {
	return &RouterPrefix{
		basePath: basePath,
		router:   p.router,
		Handlers: p.Handlers,
	}
}

func (p *RouterPrefix) Handle(method, pattern string, handlers ...HandlerFunc) {
	if len(handlers) == 0 {
		panic("there must be at least one handler")
	}

	handlers = p.combineHandlers(handlers)
	muxHandler := compose(handlers)
	p.router.Handle(method, pattern, muxHandler)
}

func copyHandlers(dst, src []HandlerFunc) {
	for index, val := range src {
		dst[index] = val
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"bytes"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const COMMAND_TIMEOUT = "1s"

var _ = Describe("bbl", func() {
	Describe("bbl -h", func() {
		It("prints out the usage", func() {
			command := exec.Command(pathToBBL, "-h")
			output := bytes.NewBuffer([]byte{})
			command.Stdout = output

			Eventually(command.Run, COMMAND_TIMEOUT, COMMAND_TIMEOUT).Should(Succeed())
			Expect(output).To(ContainSubstring("Usage"))
		})
	})

	Describe("bbl -v", func() {
		It("prints out the current version", func() {
			command := exec.Command(pathToBBL, "-v")
			output := bytes.NewBuffer([]byte{})
			command.Stdout = output

			Eventually(command.Run, COMMAND_TIMEOUT, COMMAND_TIMEOUT).Should(Succeed())
			Expect(output).To(ContainSubstring("bbl 0.0.1"))
		})
	})

	It("prints an error when an unknown flag is provided", func() {
		command := exec.Command(pathToBBL, "--some-unknown-flag")
		errors := bytes.NewBuffer([]byte{})
		command.Stderr = errors

		Eventually(command.Run, COMMAND_TIMEOUT, COMMAND_TIMEOUT).ShouldNot(Succeed())
		Expect(errors).To(ContainSubstring("unknown flag `some-unknown-flag'"))
	})
})
<commit_msg>Use gexec to invoke `bbl` in test runs<commit_after>package main_test

import (
	"os/exec"

	"github.com/gomega/gexec"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("bbl", func() {
	Describe("bbl -h", func() {
		It("prints out the usage", func() {
			session, err := gexec.Start(exec.Command(pathToBBL, "-h"), GinkgoWriter, GinkgoWriter)

			Expect(err).NotTo(HaveOccurred())
			Eventually(session).Should(gexec.Exit(0))
			Expect(session.Out.Contents()).To(ContainSubstring("usage"))
		})
	})

	Describe("bbl -v", func() {
		It("prints out the current version", func() {
			session, err := gexec.Start(exec.Command(pathToBBL, "-v"), GinkgoWriter, GinkgoWriter)

			Expect(err).NotTo(HaveOccurred())
			Eventually(session).Should(gexec.Exit(0))
			Expect(session.Out.Contents()).To(ContainSubstring("bbl 0.0.1"))
		})
	})

	It("prints an error when an unknown flag is provided", func() {
		session, err := gexec.Start(exec.Command(pathToBBL, "--some-unknown-flag"), GinkgoWriter, GinkgoWriter)

		Expect(err).NotTo(HaveOccurred())
		Eventually(session).Should(gexec.Exit(1))
		Expect(session.Err.Contents()).To(ContainSubstring("unknown flag `some-unknown-flag'"))
	})
})
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/cipher"
	"io"
	"math/rand"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/bytespool"
	"v2ray.com/core/common/protocol"
)

type BytesGenerator func() []byte

func GenerateEmptyBytes() BytesGenerator {
	var b [1]byte
	return func() []byte {
		return b[:0]
	}
}

func GenerateStaticBytes(content []byte) BytesGenerator {
	return func() []byte {
		return content
	}
}

func GenerateIncreasingNonce(nonce []byte) BytesGenerator {
	c := append([]byte(nil), nonce...)
	return func() []byte {
		for i := range c {
			c[i]++
			if c[i] != 0 {
				break
			}
		}
		return c
	}
}

func GenerateInitialAEADNonce() BytesGenerator {
	return GenerateIncreasingNonce([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
}

type Authenticator interface {
	NonceSize() int
	Overhead() int
	Open(dst, cipherText []byte) ([]byte, error)
	Seal(dst, plainText []byte) ([]byte, error)
}

type AEADAuthenticator struct {
	cipher.AEAD
	NonceGenerator          BytesGenerator
	AdditionalDataGenerator BytesGenerator
}

func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) {
	iv := v.NonceGenerator()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, newError("invalid AEAD nonce size: ", len(iv))
	}

	var additionalData []byte
	if v.AdditionalDataGenerator != nil {
		additionalData = v.AdditionalDataGenerator()
	}
	return v.AEAD.Open(dst, iv, cipherText, additionalData)
}

func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
	iv := v.NonceGenerator()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, newError("invalid AEAD nonce size: ", len(iv))
	}

	var additionalData []byte
	if v.AdditionalDataGenerator != nil {
		additionalData = v.AdditionalDataGenerator()
	}
	return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
}

type AuthenticationReader struct {
	auth         Authenticator
	reader       *buf.BufferedReader
	sizeParser   ChunkSizeDecoder
	sizeBytes    []byte
	transferType protocol.TransferType
	padding      PaddingLengthGenerator
	size         uint16
	paddingLen   uint16
	hasSize      bool
	done         bool
}

func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType, paddingLen PaddingLengthGenerator) *AuthenticationReader {
	r := &AuthenticationReader{
		auth:         auth,
		sizeParser:   sizeParser,
		transferType: transferType,
		padding:      paddingLen,
		sizeBytes:    make([]byte, sizeParser.SizeBytes()),
	}
	if breader, ok := reader.(*buf.BufferedReader); ok {
		r.reader = breader
	} else {
		r.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)}
	}
	return r
}

func (r *AuthenticationReader) readSize() (uint16, uint16, error) {
	if r.hasSize {
		r.hasSize = false
		return r.size, r.paddingLen, nil
	}
	if _, err := io.ReadFull(r.reader, r.sizeBytes); err != nil {
		return 0, 0, err
	}
	var padding uint16
	if r.padding != nil {
		padding = r.padding.NextPaddingLen()
	}
	size, err := r.sizeParser.Decode(r.sizeBytes)
	return size, padding, err
}

var errSoft = newError("waiting for more data")

func (r *AuthenticationReader) readBuffer(size int32, padding int32) (*buf.Buffer, error) {
	b := buf.New()
	if _, err := b.ReadFullFrom(r.reader, size); err != nil {
		b.Release()
		return nil, err
	}
	size -= padding
	rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size))
	if err != nil {
		b.Release()
		return nil, err
	}
	b.Resize(0, int32(len(rb)))
	return b, nil
}

func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) error {
	if soft && r.reader.BufferedBytes() < r.sizeParser.SizeBytes() {
		return errSoft
	}

	if r.done {
		return io.EOF
	}

	size, padding, err := r.readSize()
	if err != nil {
		return err
	}

	if size == uint16(r.auth.Overhead())+padding {
		r.done = true
		return io.EOF
	}

	if soft && int32(size) > r.reader.BufferedBytes() {
		r.size = size
		r.paddingLen = padding
		r.hasSize = true
		return errSoft
	}

	if size <= buf.Size {
		b, err := r.readBuffer(int32(size), int32(padding))
		if err != nil {
			return nil
		}
		mb.Append(b)
		return nil
	}

	payload := bytespool.Alloc(int32(size))
	defer bytespool.Free(payload)

	if _, err := io.ReadFull(r.reader, payload[:size]); err != nil {
		return err
	}

	size -= padding

	rb, err := r.auth.Open(payload[:0], payload[:size])
	if err != nil {
		return err
	}

	common.Must2(mb.Write(rb))
	return nil
}

func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
	const readSize = 16
	mb := buf.NewMultiBufferCap(readSize)
	if err := r.readInternal(false, &mb); err != nil {
		mb.Release()
		return nil, err
	}

	for i := 1; i < readSize; i++ {
		err := r.readInternal(true, &mb)
		if err == errSoft || err == io.EOF {
			break
		}
		if err != nil {
			mb.Release()
			return nil, err
		}
	}

	return mb, nil
}

type AuthenticationWriter struct {
	auth         Authenticator
	writer       buf.Writer
	sizeParser   ChunkSizeEncoder
	transferType protocol.TransferType
	padding      PaddingLengthGenerator
	randReader   *rand.Rand
}

func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType, padding PaddingLengthGenerator) *AuthenticationWriter {
	w := &AuthenticationWriter{
		auth:         auth,
		writer:       buf.NewWriter(writer),
		sizeParser:   sizeParser,
		transferType: transferType,
	}
	if padding != nil {
		w.padding = padding
		w.randReader = rand.New(rand.NewSource(time.Now().Unix()))
	}
	return w
}

func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
	encryptedSize := b.Len() + int32(w.auth.Overhead())
	var paddingSize int32
	if w.padding != nil {
		paddingSize = int32(w.padding.NextPaddingLen())
	}

	totalSize := encryptedSize + paddingSize
	if totalSize > buf.Size {
		return nil, newError("size too large: ", totalSize)
	}

	eb := buf.New()
	w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(w.sizeParser.SizeBytes()))
	if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b.Bytes()); err != nil {
		eb.Release()
		return nil, err
	}
	if paddingSize > 0 {
		// With size of the chunk and padding length encrypted, the content of padding doesn't matter much.
		common.Must2(eb.ReadFullFrom(w.randReader, int32(paddingSize)))
	}

	return eb, nil
}

func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
	defer mb.Release()

	var maxPadding int32
	if w.padding != nil {
		maxPadding = int32(w.padding.MaxPaddingLen())
	}

	payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() - maxPadding
	mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10))

	for {
		b := buf.New()
		common.Must2(b.ReadFrom(io.LimitReader(&mb, int64(payloadSize))))
		eb, err := w.seal(b)
		b.Release()

		if err != nil {
			mb2Write.Release()
			return err
		}
		mb2Write.Append(eb)
		if mb.IsEmpty() {
			break
		}
	}

	return w.writer.WriteMultiBuffer(mb2Write)
}

func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
	defer mb.Release()

	mb2Write := buf.NewMultiBufferCap(int32(len(mb)) + 1)

	for _, b := range mb {
		if b.IsEmpty() {
			continue
		}

		eb, err := w.seal(b)
		if err != nil {
			continue
		}

		mb2Write.Append(eb)
	}

	if mb2Write.IsEmpty() {
		return nil
	}

	return w.writer.WriteMultiBuffer(mb2Write)
}

// WriteMultiBuffer implements buf.Writer.
func (w *AuthenticationWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
	if mb.IsEmpty() {
		b := buf.New()
		defer b.Release()

		eb, err := w.seal(b)
		common.Must(err)
		return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(eb))
	}

	if w.transferType == protocol.TransferTypeStream {
		return w.writeStream(mb)
	}

	return w.writePacket(mb)
}
<commit_msg>share rand reader across auth readers<commit_after>package crypto

import (
	"crypto/cipher"
	"io"
	"math/rand"

	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/bytespool"
	"v2ray.com/core/common/protocol"
)

type BytesGenerator func() []byte

func GenerateEmptyBytes() BytesGenerator {
	var b [1]byte
	return func() []byte {
		return b[:0]
	}
}

func GenerateStaticBytes(content []byte) BytesGenerator {
	return func() []byte {
		return content
	}
}

func GenerateIncreasingNonce(nonce []byte) BytesGenerator {
	c := append([]byte(nil), nonce...)
	return func() []byte {
		for i := range c {
			c[i]++
			if c[i] != 0 {
				break
			}
		}
		return c
	}
}

func GenerateInitialAEADNonce() BytesGenerator {
	return GenerateIncreasingNonce([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
}

type Authenticator interface {
	NonceSize() int
	Overhead() int
	Open(dst, cipherText []byte) ([]byte, error)
	Seal(dst, plainText []byte) ([]byte, error)
}

type AEADAuthenticator struct {
	cipher.AEAD
	NonceGenerator          BytesGenerator
	AdditionalDataGenerator BytesGenerator
}

func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) {
	iv := v.NonceGenerator()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, newError("invalid AEAD nonce size: ", len(iv))
	}

	var additionalData []byte
	if v.AdditionalDataGenerator != nil {
		additionalData = v.AdditionalDataGenerator()
	}
	return v.AEAD.Open(dst, iv, cipherText, additionalData)
}

func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
	iv := v.NonceGenerator()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, newError("invalid AEAD nonce size: ", len(iv))
	}

	var additionalData []byte
	if v.AdditionalDataGenerator != nil {
		additionalData = v.AdditionalDataGenerator()
	}
	return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
}

type AuthenticationReader struct {
	auth         Authenticator
	reader       *buf.BufferedReader
	sizeParser   ChunkSizeDecoder
	sizeBytes    []byte
	transferType protocol.TransferType
	padding      PaddingLengthGenerator
	size         uint16
	paddingLen   uint16
	hasSize      bool
	done         bool
}

func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType, paddingLen PaddingLengthGenerator) *AuthenticationReader {
	r := &AuthenticationReader{
		auth:         auth,
		sizeParser:   sizeParser,
		transferType: transferType,
		padding:      paddingLen,
		sizeBytes:    make([]byte, sizeParser.SizeBytes()),
	}
	if breader, ok := reader.(*buf.BufferedReader); ok {
		r.reader = breader
	} else {
		r.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)}
	}
	return r
}

func (r *AuthenticationReader) readSize() (uint16, uint16, error) {
	if r.hasSize {
		r.hasSize = false
		return r.size, r.paddingLen, nil
	}
	if _, err := io.ReadFull(r.reader, r.sizeBytes); err != nil {
		return 0, 0, err
	}
	var padding uint16
	if r.padding != nil {
		padding = r.padding.NextPaddingLen()
	}
	size, err := r.sizeParser.Decode(r.sizeBytes)
	return size, padding, err
}

var errSoft = newError("waiting for more data")

func (r *AuthenticationReader) readBuffer(size int32, padding int32) (*buf.Buffer, error) {
	b := buf.New()
	if _, err := b.ReadFullFrom(r.reader, size); err != nil {
		b.Release()
		return nil, err
	}
	size -= padding
	rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size))
	if err != nil {
		b.Release()
		return nil, err
	}
	b.Resize(0, int32(len(rb)))
	return b, nil
}

func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) error {
	if soft && r.reader.BufferedBytes() < r.sizeParser.SizeBytes() {
		return errSoft
	}

	if r.done {
		return io.EOF
	}

	size, padding, err := r.readSize()
	if err != nil {
		return err
	}

	if size == uint16(r.auth.Overhead())+padding {
		r.done = true
		return io.EOF
	}

	if soft && int32(size) > r.reader.BufferedBytes() {
		r.size = size
		r.paddingLen = padding
		r.hasSize = true
		return errSoft
	}

	if size <= buf.Size {
		b, err := r.readBuffer(int32(size), int32(padding))
		if err != nil {
			return nil
		}
		mb.Append(b)
		return nil
	}

	payload := bytespool.Alloc(int32(size))
	defer bytespool.Free(payload)

	if _, err := io.ReadFull(r.reader, payload[:size]); err != nil {
		return err
	}

	size -= padding

	rb, err := r.auth.Open(payload[:0], payload[:size])
	if err != nil {
		return err
	}

	common.Must2(mb.Write(rb))
	return nil
}

func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
	const readSize = 16
	mb := buf.NewMultiBufferCap(readSize)
	if err := r.readInternal(false, &mb); err != nil {
		mb.Release()
		return nil, err
	}

	for i := 1; i < readSize; i++ {
		err := r.readInternal(true, &mb)
		if err == errSoft || err == io.EOF {
			break
		}
		if err != nil {
			mb.Release()
			return nil, err
		}
	}

	return mb, nil
}

type AuthenticationWriter struct {
	auth         Authenticator
	writer       buf.Writer
	sizeParser   ChunkSizeEncoder
	transferType protocol.TransferType
	padding      PaddingLengthGenerator
}

func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType, padding PaddingLengthGenerator) *AuthenticationWriter {
	w := &AuthenticationWriter{
		auth:         auth,
		writer:       buf.NewWriter(writer),
		sizeParser:   sizeParser,
		transferType: transferType,
	}
	if padding != nil {
		w.padding = padding
	}
	return w
}

func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
	encryptedSize := b.Len() + int32(w.auth.Overhead())
	var paddingSize int32
	if w.padding != nil {
		paddingSize = int32(w.padding.NextPaddingLen())
	}

	totalSize := encryptedSize + paddingSize
	if totalSize > buf.Size {
		return nil, newError("size too large: ", totalSize)
	}

	eb := buf.New()
	w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(w.sizeParser.SizeBytes()))
	if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b.Bytes()); err != nil {
		eb.Release()
		return nil, err
	}
	if paddingSize > 0 {
		// With size of the chunk and padding length encrypted, the content of padding doesn't matter much.
		paddingBytes := eb.Extend(paddingSize)
		common.Must2(rand.Read(paddingBytes))
	}

	return eb, nil
}

func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
	defer mb.Release()

	var maxPadding int32
	if w.padding != nil {
		maxPadding = int32(w.padding.MaxPaddingLen())
	}

	payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() - maxPadding
	mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10))

	for {
		b := buf.New()
		common.Must2(b.ReadFrom(io.LimitReader(&mb, int64(payloadSize))))
		eb, err := w.seal(b)
		b.Release()

		if err != nil {
			mb2Write.Release()
			return err
		}
		mb2Write.Append(eb)
		if mb.IsEmpty() {
			break
		}
	}

	return w.writer.WriteMultiBuffer(mb2Write)
}

func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
	defer mb.Release()

	mb2Write := buf.NewMultiBufferCap(int32(len(mb)) + 1)

	for _, b := range mb {
		if b.IsEmpty() {
			continue
		}

		eb, err := w.seal(b)
		if err != nil {
			continue
		}

		mb2Write.Append(eb)
	}

	if mb2Write.IsEmpty() {
		return nil
	}

	return w.writer.WriteMultiBuffer(mb2Write)
}

// WriteMultiBuffer implements buf.Writer.
func (w *AuthenticationWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
	if mb.IsEmpty() {
		b := buf.New()
		defer b.Release()

		eb, err := w.seal(b)
		common.Must(err)
		return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(eb))
	}

	if w.transferType == protocol.TransferTypeStream {
		return w.writeStream(mb)
	}

	return w.writePacket(mb)
}
<|endoftext|>


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

package tsmon

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

	"golang.org/x/net/context"

	"github.com/luci/luci-go/common/auth"
	"github.com/luci/luci-go/common/gcloud/pubsub"
	"github.com/luci/luci-go/common/logging"
	"github.com/luci/luci-go/common/tsmon/monitor"
	"github.com/luci/luci-go/common/tsmon/store"
	"github.com/luci/luci-go/common/tsmon/target"
	"github.com/luci/luci-go/common/tsmon/types"
)

var (
	globalTarget  types.Target
	globalStore   = store.NewNilStore()
	globalMonitor = monitor.NewNilMonitor()
	globalFlusher *autoFlusher

	registeredMetrics     = map[string]types.Metric{}
	registeredMetricsLock sync.RWMutex
)

// Target contains information about this process, and is included in all
// metrics reported by this process.
func Target() types.Target {
	return globalTarget
}

// Store returns the global metric store that contains all the metric values for
// this process.  Applications shouldn't need to access this directly - instead
// use the metric objects which provide type-safe accessors.
func Store() store.Store {
	return globalStore
}

// Monitor returns the global monitor that sends metrics to monitoring
// endpoints.  Defaults to a nil monitor, but changed by InitializeFromFlags.
func Monitor() monitor.Monitor {
	return globalMonitor
}

// Register is called by metric objects to register themselves.  This will panic
// if another metric with the same name is already registered.
func Register(m types.Metric) {
	registeredMetricsLock.Lock()
	defer registeredMetricsLock.Unlock()

	if _, ok := registeredMetrics[m.Info().Name]; ok {
		panic(fmt.Sprintf("A metric with the name '%s' was already registered", m.Info().Name))
	}

	registeredMetrics[m.Info().Name] = m

	if globalStore != nil {
		globalStore.Register(m)
	}
}

// Unregister is called by metric objects to unregister themselves.
func Unregister(m types.Metric) {
	registeredMetricsLock.Lock()
	defer registeredMetricsLock.Unlock()

	delete(registeredMetrics, m.Info().Name)

	if globalStore != nil {
		globalStore.Unregister(m)
	}
}

// SetStore changes the global metric store.  All metrics that were registered
// with the old store will be re-registered on the new store.
func SetStore(s store.Store) {
	if s == globalStore {
		return
	}

	registeredMetricsLock.RLock()
	defer registeredMetricsLock.RUnlock()

	// Register metrics on the new store.
	for _, m := range registeredMetrics {
		s.Register(m)
	}

	oldStore := globalStore
	globalStore = s

	// Unregister metrics from the old store.
	if oldStore != nil {
		for _, m := range registeredMetrics {
			globalStore.Unregister(m)
		}
	}
}

// InitializeFromFlags configures the tsmon library from flag values.
//
// This will set a Target (information about what's reporting metrics) and a
// Monitor (where to send the metrics to).
func InitializeFromFlags(c context.Context, fl *Flags) error {
	mon, err := initMonitor(c, fl)
	switch {
	case err != nil:
		return err
	case mon == nil:
		return nil // tsmon is disabled
	}

	// Monitoring is enabled, so get the expensive default values for hostname,
	// etc.
	fl.Target.SetDefaultsFromHostname()
	t, err := target.NewFromFlags(&fl.Target)
	if err != nil {
		return err
	}

	globalMonitor = mon
	globalTarget = t
	SetStore(store.NewInMemory())

	if globalFlusher != nil {
		logging.Infof(c, "Canceling previous tsmon auto flush")
		globalFlusher.stop()
		globalFlusher = nil
	}

	if fl.Flush == "auto" {
		globalFlusher = &autoFlusher{}
		globalFlusher.start(c, fl.FlushInterval)
	}

	return nil
}

// Shutdown gracefully terminates the tsmon by doing the final flush and
// disabling auto flush (if it was enabled).
//
// It resets Target, Monitor and Store.
//
// Logs error to standard logger. Does nothing if tsmon wasn't initialized.
func Shutdown(c context.Context) {
	if store.IsNilStore(globalStore) {
		return
	}

	if globalFlusher != nil {
		logging.Debugf(c, "Stopping tsmon auto flush")
		globalFlusher.stop()
		globalFlusher = nil
	}

	logging.Debugf(c, "Doing the final tsmon flush")
	if err := Flush(c); err != nil {
		logging.Errorf(c, "Final tsmon flush failed - %s", err)
	} else {
		logging.Debugf(c, "Final tsmon flush finished")
	}

	// Reset the state as if 'InitializeFromFlags' was never called.
	globalMonitor = monitor.NewNilMonitor()
	globalTarget = nil
	SetStore(store.NewNilStore())
}

// initMonitor examines flags and config and initializes a monitor.
//
// It returns (nil, nil) if tsmon should be disabled.
func initMonitor(c context.Context, fl *Flags) (monitor.Monitor, error) {
	// Load the config file, and override its values with flags.
	config, err := loadConfig(fl.ConfigFile)
	if err != nil {
		logging.Warningf(c, "tsmon is disabled because the config file (%s) could not be loaded: %s",
			fl.ConfigFile, err)
		return nil, nil
	}

	if fl.Endpoint != "" {
		config.Endpoint = fl.Endpoint
	}
	if fl.Credentials != "" {
		config.Credentials = fl.Credentials
	}

	if config.Endpoint == "" {
		logging.Warningf(c, "tsmon is disabled because no endpoint is configured")
		return nil, nil
	}

	endpointURL, err := url.Parse(config.Endpoint)
	if err != nil {
		return nil, err
	}

	switch endpointURL.Scheme {
	case "file":
		return monitor.NewDebugMonitor(logging.Get(c), endpointURL.Path), nil
	case "pubsub":
		cl, err := makeClient(c, config.Credentials)
		if err != nil {
			return nil, err
		}
		return monitor.NewPubsubMonitor(c, cl, endpointURL.Host, strings.TrimPrefix(endpointURL.Path, "/"))
	default:
		return nil, fmt.Errorf("unknown tsmon endpoint url: %s", config.Endpoint)
	}
}

// makeClient returns http.Client that knows how to send authenticated requests
// to PubSub API.
func makeClient(c context.Context, credentials string) (*http.Client, error) {
	authOpts := auth.Options{
		Context: c,
		Scopes:  pubsub.PublisherScopes,
	}
	if credentials == GCECredentials {
		authOpts.Method = auth.GCEMetadataMethod
	} else {
		authOpts.Method = auth.ServiceAccountMethod
		authOpts.ServiceAccountJSONPath = credentials
	}
	return auth.NewAuthenticator(auth.SilentLogin, authOpts).Client()
}
<commit_msg>Disable tsmon when the endpoint is set to 'none'.<commit_after>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package tsmon

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

	"golang.org/x/net/context"

	"github.com/luci/luci-go/common/auth"
	"github.com/luci/luci-go/common/gcloud/pubsub"
	"github.com/luci/luci-go/common/logging"
	"github.com/luci/luci-go/common/tsmon/monitor"
	"github.com/luci/luci-go/common/tsmon/store"
	"github.com/luci/luci-go/common/tsmon/target"
	"github.com/luci/luci-go/common/tsmon/types"
)

var (
	globalTarget  types.Target
	globalStore   = store.NewNilStore()
	globalMonitor = monitor.NewNilMonitor()
	globalFlusher *autoFlusher

	registeredMetrics     = map[string]types.Metric{}
	registeredMetricsLock sync.RWMutex
)

// Target contains information about this process, and is included in all
// metrics reported by this process.
func Target() types.Target {
	return globalTarget
}

// Store returns the global metric store that contains all the metric values for
// this process.  Applications shouldn't need to access this directly - instead
// use the metric objects which provide type-safe accessors.
func Store() store.Store {
	return globalStore
}

// Monitor returns the global monitor that sends metrics to monitoring
// endpoints.  Defaults to a nil monitor, but changed by InitializeFromFlags.
func Monitor() monitor.Monitor {
	return globalMonitor
}

// Register is called by metric objects to register themselves.  This will panic
// if another metric with the same name is already registered.
func Register(m types.Metric) {
	registeredMetricsLock.Lock()
	defer registeredMetricsLock.Unlock()

	if _, ok := registeredMetrics[m.Info().Name]; ok {
		panic(fmt.Sprintf("A metric with the name '%s' was already registered", m.Info().Name))
	}

	registeredMetrics[m.Info().Name] = m

	if globalStore != nil {
		globalStore.Register(m)
	}
}

// Unregister is called by metric objects to unregister themselves.
func Unregister(m types.Metric) {
	registeredMetricsLock.Lock()
	defer registeredMetricsLock.Unlock()

	delete(registeredMetrics, m.Info().Name)

	if globalStore != nil {
		globalStore.Unregister(m)
	}
}

// SetStore changes the global metric store.  All metrics that were registered
// with the old store will be re-registered on the new store.
func SetStore(s store.Store) {
	if s == globalStore {
		return
	}

	registeredMetricsLock.RLock()
	defer registeredMetricsLock.RUnlock()

	// Register metrics on the new store.
	for _, m := range registeredMetrics {
		s.Register(m)
	}

	oldStore := globalStore
	globalStore = s

	// Unregister metrics from the old store.
	if oldStore != nil {
		for _, m := range registeredMetrics {
			globalStore.Unregister(m)
		}
	}
}

// InitializeFromFlags configures the tsmon library from flag values.
//
// This will set a Target (information about what's reporting metrics) and a
// Monitor (where to send the metrics to).
func InitializeFromFlags(c context.Context, fl *Flags) error {
	mon, err := initMonitor(c, fl)
	switch {
	case err != nil:
		return err
	case mon == nil:
		return nil // tsmon is disabled
	}

	// Monitoring is enabled, so get the expensive default values for hostname,
	// etc.
	fl.Target.SetDefaultsFromHostname()
	t, err := target.NewFromFlags(&fl.Target)
	if err != nil {
		return err
	}

	globalMonitor = mon
	globalTarget = t
	SetStore(store.NewInMemory())

	if globalFlusher != nil {
		logging.Infof(c, "Canceling previous tsmon auto flush")
		globalFlusher.stop()
		globalFlusher = nil
	}

	if fl.Flush == "auto" {
		globalFlusher = &autoFlusher{}
		globalFlusher.start(c, fl.FlushInterval)
	}

	return nil
}

// Shutdown gracefully terminates the tsmon by doing the final flush and
// disabling auto flush (if it was enabled).
//
// It resets Target, Monitor and Store.
//
// Logs error to standard logger. Does nothing if tsmon wasn't initialized.
func Shutdown(c context.Context) {
	if store.IsNilStore(globalStore) {
		return
	}

	if globalFlusher != nil {
		logging.Debugf(c, "Stopping tsmon auto flush")
		globalFlusher.stop()
		globalFlusher = nil
	}

	logging.Debugf(c, "Doing the final tsmon flush")
	if err := Flush(c); err != nil {
		logging.Errorf(c, "Final tsmon flush failed - %s", err)
	} else {
		logging.Debugf(c, "Final tsmon flush finished")
	}

	// Reset the state as if 'InitializeFromFlags' was never called.
	globalMonitor = monitor.NewNilMonitor()
	globalTarget = nil
	SetStore(store.NewNilStore())
}

// initMonitor examines flags and config and initializes a monitor.
//
// It returns (nil, nil) if tsmon should be disabled.
func initMonitor(c context.Context, fl *Flags) (monitor.Monitor, error) {
	// Load the config file, and override its values with flags.
	config, err := loadConfig(fl.ConfigFile)
	if err != nil {
		logging.Infof(c, "tsmon is disabled because the config file (%s) could not be loaded: %s",
			fl.ConfigFile, err)
		return nil, nil
	}

	if fl.Endpoint != "" {
		config.Endpoint = fl.Endpoint
	}
	if fl.Credentials != "" {
		config.Credentials = fl.Credentials
	}

	if config.Endpoint == "" {
		logging.Infof(c, "tsmon is disabled because no endpoint is configured")
		return nil, nil
	}
	if strings.ToLower(config.Endpoint) == "none" {
		logging.Infof(c, "tsmon is explicitly disabled ")
		return nil, nil
	}

	endpointURL, err := url.Parse(config.Endpoint)
	if err != nil {
		return nil, err
	}

	switch endpointURL.Scheme {
	case "file":
		return monitor.NewDebugMonitor(logging.Get(c), endpointURL.Path), nil
	case "pubsub":
		cl, err := makeClient(c, config.Credentials)
		if err != nil {
			return nil, err
		}
		return monitor.NewPubsubMonitor(c, cl, endpointURL.Host, strings.TrimPrefix(endpointURL.Path, "/"))
	default:
		return nil, fmt.Errorf("unknown tsmon endpoint url: %s", config.Endpoint)
	}
}

// makeClient returns http.Client that knows how to send authenticated requests
// to PubSub API.
func makeClient(c context.Context, credentials string) (*http.Client, error) {
	authOpts := auth.Options{
		Context: c,
		Scopes:  pubsub.PublisherScopes,
	}
	if credentials == GCECredentials {
		authOpts.Method = auth.GCEMetadataMethod
	} else {
		authOpts.Method = auth.ServiceAccountMethod
		authOpts.ServiceAccountJSONPath = credentials
	}
	return auth.NewAuthenticator(auth.SilentLogin, authOpts).Client()
}
<|endoftext|>


<commit_before>package models_test

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/pivotal-golang/lager/lagertest"

	"cred-alert/models"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)

var _ = Describe("Database Connections", func() {
	var (
		db           *gorm.DB
		dbFileHandle *os.File
		logger       *lagertest.TestLogger
	)

	BeforeEach(func() {
		var err error
		dbFileHandle, err = ioutil.TempFile("", "test.db")
		Expect(err).NotTo(HaveOccurred())

		err = dbFileHandle.Close()
		Expect(err).NotTo(HaveOccurred())

		db, err = gorm.Open("sqlite3", dbFileHandle.Name())
		Expect(err).NotTo(HaveOccurred())
		db.AutoMigrate(&models.DiffScan{}, &models.Commit{})
		logger = lagertest.NewTestLogger("commit-repository")
	})

	AfterEach(func() {
		db.Close()
		os.Remove(dbFileHandle.Name())
	})

	Describe("auto-migrations", func() {
		It("creates the DiffScan table", func() {
			Expect(db.HasTable(&models.DiffScan{})).To(BeTrue())
		})

		It("creates the Commit table", func() {
			Expect(db.HasTable(&models.Commit{})).To(BeTrue())
		})
	})

	Describe("CommitRepository", func() {
		var (
			commitRepository models.CommitRepository
			fakeCommit       *models.Commit
		)

		BeforeEach(func() {
			// commitRepository = models.NewCommitRepository(fakeDB)
			commitRepository = models.NewCommitRepository(db)
			fakeCommit = &models.Commit{
				SHA:       "abc123",
				Timestamp: time.Now(),
				Org:       "my-org",
				Repo:      "my-repo",
			}
		})

		Describe("RegisterCommit", func() {
			It("Saves a commit to the db", func() {
				commitRepository.RegisterCommit(logger, fakeCommit)
				// Expect(fakeDB.SaveCallCount()).To(Equal(1))
				// savedCommit := fakeDB.SaveArgsForCall(0)
				var savedCommit *models.Commit
				savedCommit = &models.Commit{}
				db.Last(&savedCommit)
				Expect(savedCommit.SHA).To(Equal(fakeCommit.SHA))
				Expect(savedCommit.Org).To(Equal(fakeCommit.Org))
				Expect(savedCommit.Repo).To(Equal(fakeCommit.Repo))
				Expect(savedCommit.Timestamp.Unix()).To(Equal(fakeCommit.Timestamp.Unix()))
			})

			It("returns any error", func() {
				saveError := errors.New("save error")
				db.AddError(saveError)
				err := commitRepository.RegisterCommit(logger, fakeCommit)
				Expect(err).To(HaveOccurred())
				Expect(err).To(Equal(saveError))
			})
		})

		Describe("IsCommitRegistered", func() {
			It("Returns true if a commit has been registered", func() {
				commitRepository.RegisterCommit(logger, fakeCommit)
				isRegistered, err := commitRepository.IsCommitRegistered(logger, "abc123")
				Expect(err).ToNot(HaveOccurred())
				Expect(isRegistered).To(BeTrue())
			})

			It("Returns false if a commit has not been registered", func() {
				commitRepository.RegisterCommit(logger, fakeCommit)
				isRegistered, err := commitRepository.IsCommitRegistered(logger, "wrong-sha")
				Expect(err).ToNot(HaveOccurred())
				Expect(isRegistered).To(BeFalse())
			})

			It("Returns any errors", func() {
				findError := errors.New("find commit error")
				db.AddError(findError)
				_, err := commitRepository.IsCommitRegistered(logger, "abc123")
				Expect(err).To(HaveOccurred())
				Expect(err).To(Equal(findError))
			})
		})
	})

	Describe("DiffScanRepository", func() {
		var (
			diffScanRepository models.DiffScanRepository
			fakeDiffScan       *models.DiffScan
			taskID             string
		)

		BeforeEach(func() {
			diffScanRepository = models.NewDiffScanRepository(db)
			taskID = "some-guid"
			fakeDiffScan = &models.DiffScan{
				Org:             "my-org",
				Repo:            "my-repo",
				FromCommit:      "sha-1",
				ToCommit:        "sha-2",
				Timestamp:       time.Now(),
				TaskID:          taskID,
				CredentialFound: false,
			}
		})

		It("Saves a diff scan", func() {
			err := diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(err).ToNot(HaveOccurred())
			var diffs []models.DiffScan
			err = db.Where(&models.DiffScan{TaskID: taskID}).First(&diffs).Error
			Expect(err).ToNot(HaveOccurred())
			Expect(diffs).To(HaveLen(1))
			Expect(diffs[0].TaskID).To(Equal(taskID))
		})

		It("Returns any error", func() {
			findError := errors.New("save diff error")
			db.AddError(findError)
			err := diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(err).To(HaveOccurred())
			Expect(err).To(Equal(findError))
		})

		It("should log successfully saving", func() {
			diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(logger).To(gbytes.Say("saving-diffscan"))
			Expect(logger).To(gbytes.Say("successfully-saved-diffscan"))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"credential-found":%v`, fakeDiffScan.CredentialFound)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"from-commit":"%s"`, fakeDiffScan.FromCommit)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"org":"%s"`, fakeDiffScan.Org)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"repo":"%s"`, fakeDiffScan.Repo)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"scan-timestamp":%d`, fakeDiffScan.Timestamp.Unix())))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"task-id":"%s"`, fakeDiffScan.TaskID)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"to-commit":"%s"`, fakeDiffScan.ToCommit)))
		})

		It("should log error saving", func() {
			findError := errors.New("save diff error")
			db.AddError(findError)
			diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(logger).To(gbytes.Say("error-saving-diffscan"))
		})
	})
})
<commit_msg>quiet database logging in tests<commit_after>package models_test

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/pivotal-golang/lager/lagertest"

	"cred-alert/models"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)

var _ = Describe("Database Connections", func() {
	var (
		db           *gorm.DB
		dbFileHandle *os.File
		logger       *lagertest.TestLogger
	)

	BeforeEach(func() {
		logger = lagertest.NewTestLogger("commit-repository")

		var err error
		dbFileHandle, err = ioutil.TempFile("", "test.db")
		Expect(err).NotTo(HaveOccurred())

		err = dbFileHandle.Close()
		Expect(err).NotTo(HaveOccurred())

		db, err = gorm.Open("sqlite3", dbFileHandle.Name())
		Expect(err).NotTo(HaveOccurred())
		db.AutoMigrate(&models.DiffScan{}, &models.Commit{})
		db.LogMode(false)
	})

	AfterEach(func() {
		db.Close()
		os.Remove(dbFileHandle.Name())
	})

	Describe("auto-migrations", func() {
		It("creates the DiffScan table", func() {
			Expect(db.HasTable(&models.DiffScan{})).To(BeTrue())
		})

		It("creates the Commit table", func() {
			Expect(db.HasTable(&models.Commit{})).To(BeTrue())
		})
	})

	Describe("CommitRepository", func() {
		var (
			commitRepository models.CommitRepository
			fakeCommit       *models.Commit
		)

		BeforeEach(func() {
			// commitRepository = models.NewCommitRepository(fakeDB)
			commitRepository = models.NewCommitRepository(db)
			fakeCommit = &models.Commit{
				SHA:       "abc123",
				Timestamp: time.Now(),
				Org:       "my-org",
				Repo:      "my-repo",
			}
		})

		Describe("RegisterCommit", func() {
			It("Saves a commit to the db", func() {
				commitRepository.RegisterCommit(logger, fakeCommit)
				// Expect(fakeDB.SaveCallCount()).To(Equal(1))
				// savedCommit := fakeDB.SaveArgsForCall(0)
				var savedCommit *models.Commit
				savedCommit = &models.Commit{}
				db.Last(&savedCommit)
				Expect(savedCommit.SHA).To(Equal(fakeCommit.SHA))
				Expect(savedCommit.Org).To(Equal(fakeCommit.Org))
				Expect(savedCommit.Repo).To(Equal(fakeCommit.Repo))
				Expect(savedCommit.Timestamp.Unix()).To(Equal(fakeCommit.Timestamp.Unix()))
			})

			It("returns any error", func() {
				saveError := errors.New("save error")
				db.AddError(saveError)
				err := commitRepository.RegisterCommit(logger, fakeCommit)
				Expect(err).To(HaveOccurred())
				Expect(err).To(Equal(saveError))
			})
		})

		Describe("IsCommitRegistered", func() {
			It("Returns true if a commit has been registered", func() {
				commitRepository.RegisterCommit(logger, fakeCommit)
				isRegistered, err := commitRepository.IsCommitRegistered(logger, "abc123")
				Expect(err).ToNot(HaveOccurred())
				Expect(isRegistered).To(BeTrue())
			})

			It("Returns false if a commit has not been registered", func() {
				commitRepository.RegisterCommit(logger, fakeCommit)
				isRegistered, err := commitRepository.IsCommitRegistered(logger, "wrong-sha")
				Expect(err).ToNot(HaveOccurred())
				Expect(isRegistered).To(BeFalse())
			})

			It("Returns any errors", func() {
				findError := errors.New("find commit error")
				db.AddError(findError)
				_, err := commitRepository.IsCommitRegistered(logger, "abc123")
				Expect(err).To(HaveOccurred())
				Expect(err).To(Equal(findError))
			})
		})
	})

	Describe("DiffScanRepository", func() {
		var (
			diffScanRepository models.DiffScanRepository
			fakeDiffScan       *models.DiffScan
			taskID             string
		)

		BeforeEach(func() {
			diffScanRepository = models.NewDiffScanRepository(db)
			taskID = "some-guid"
			fakeDiffScan = &models.DiffScan{
				Org:             "my-org",
				Repo:            "my-repo",
				FromCommit:      "sha-1",
				ToCommit:        "sha-2",
				Timestamp:       time.Now(),
				TaskID:          taskID,
				CredentialFound: false,
			}
		})

		It("Saves a diff scan", func() {
			err := diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(err).ToNot(HaveOccurred())
			var diffs []models.DiffScan
			err = db.Where(&models.DiffScan{TaskID: taskID}).First(&diffs).Error
			Expect(err).ToNot(HaveOccurred())
			Expect(diffs).To(HaveLen(1))
			Expect(diffs[0].TaskID).To(Equal(taskID))
		})

		It("Returns any error", func() {
			findError := errors.New("save diff error")
			db.AddError(findError)
			err := diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(err).To(HaveOccurred())
			Expect(err).To(Equal(findError))
		})

		It("should log successfully saving", func() {
			diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(logger).To(gbytes.Say("saving-diffscan"))
			Expect(logger).To(gbytes.Say("successfully-saved-diffscan"))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"credential-found":%v`, fakeDiffScan.CredentialFound)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"from-commit":"%s"`, fakeDiffScan.FromCommit)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"org":"%s"`, fakeDiffScan.Org)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"repo":"%s"`, fakeDiffScan.Repo)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"scan-timestamp":%d`, fakeDiffScan.Timestamp.Unix())))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"task-id":"%s"`, fakeDiffScan.TaskID)))
			Expect(logger).To(gbytes.Say(fmt.Sprintf(`"to-commit":"%s"`, fakeDiffScan.ToCommit)))
		})

		It("should log error saving", func() {
			findError := errors.New("save diff error")
			db.AddError(findError)
			diffScanRepository.SaveDiffScan(logger, fakeDiffScan)
			Expect(logger).To(gbytes.Say("error-saving-diffscan"))
		})
	})
})
<|endoftext|>