<commit_before>package main

import (
	"fmt"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/net/context"
	"google.golang.org/api/drive/v2"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var clearline string = fmt.Sprintf("%c[2K\r", 27)
var current_file string

func sizeToString(size int64) string {
	prefix := ""
	flsz := float64(0)

	if size > 1<<30 {
		prefix = "G"
		flsz = float64(size) / float64(1<<30)
	} else if size > 1<<20 {
		prefix = "M"
		flsz = float64(size) / float64(1<<20)
	} else if size > 1<<10 {
		prefix = "k"
		flsz = float64(size) / float64(1<<10)
	} else {
		flsz = float64(size)
	}

	return fmt.Sprintf("%.1f%s", flsz, prefix)
}

func getProgressFunction(filename string) func(done, max int64) {
	return func(done, max int64) {
		if current_file == "" {
			current_file = filename
		}
		if filename != current_file {
			current_file = filename
			fmt.Println()
		}

		width, _, _ := terminal.GetSize(0)
		bar_width := width - 33

		n_filled := int((float64(done) / float64(max)) * float64(bar_width))

		sizestr := sizeToString(done)

		if n_filled > len(sizestr) {
			n_filled -= len(sizestr)
		} else {
			n_filled = 0
		}

		n_empty := bar_width - n_filled + len(sizestr)

		filled := strings.Repeat("=", n_filled) + sizestr
		empty := strings.Repeat(" ", n_empty)

		short_filename := filename

		if len(filename) > 30 {
			short_filename = "..." + filename[len(filename)-27:len(filename)]
		}

		bar := fmt.Sprintf("%30s [%s%s]", short_filename, filled, empty)

		fmt.Print(clearline)
		fmt.Print(bar)
	}
}

func uploadFileList(cl *drive.Service, list []string) error {
	pathtoidmap := make(map[string]string)
	pathtoidmap["."] = FLAG_folder_id

	for _, file := range list {
		dir := filepath.Dir(file)

		id, ok := pathtoidmap[dir]

		if !ok {
			err := createNestedFolders(cl, pathtoidmap, FLAG_folder_id, strings.Split(dir, "/"))
			if err != nil {
				return err
			}
			id = pathtoidmap[dir]
		}

		parentref := &drive.ParentReference{Id: id}
		drive_file := &drive.File{Title: filepath.Base(file), Parents: []*drive.ParentReference{parentref}}

		data, err := os.Open(file)

		if err != nil {
			log.Print(err.Error())
			continue
		}

		stat, err := data.Stat()

		_, err = cl.Files.Insert(drive_file).
			ResumableMedia(context.Background(), data, stat.Size(), "").
			ProgressUpdater(getProgressFunction(file)).
			Do()

		data.Close()

		if err != nil {
			log.Print(err.Error())
		}
	}
	fmt.Println("")

	return nil
}

func createNestedFolders(cl *drive.Service, pathtoid map[string]string, root string, folders []string) error {
	parent := root
	path := ""

	for _, folder := range folders {
		if folder == "" || folder == "." {
			continue
		}

		path = filepath.Join(path, folder)

		next_parent, ok := pathtoid[path]

		if ok {
			parent = next_parent
			continue
		} else {

			query := "title = '" + folder + "' and '" + parent + "' in parents"

			list, err := cl.Files.List().Q(query).Do()

			if err != nil {
				return err
			}

			if len(list.Items) == 0 {
				parentref := &drive.ParentReference{Id: parent}
				file := &drive.File{MimeType: "application/vnd.google-apps.folder",
					Title: folder, Parents: []*drive.ParentReference{parentref}}

				f, err := cl.Files.Insert(file).Do()

				if err != nil {
					return err
				}
				pathtoid[path] = f.Id
				parent = f.Id
			} else {
				pathtoid[path] = list.Items[0].Id
				parent = list.Items[0].Id
			}
		}
	}

	return nil
}

func getFileList(local_folder string) []string {
	list := make([]string, 0, 16)

	st, err := os.Stat(local_folder)

	if err != nil {
		log.Fatal(err)
		return list
	}

	if !st.IsDir() {
		return append(list, local_folder)
	}

	files, err := ioutil.ReadDir(local_folder)

	if err != nil {
		log.Fatal(err)
		return list
	}

	for _, f := range files {
		if f.IsDir() {
			list = append(list, getFileList(filepath.Join(local_folder, f.Name()))...)
		} else {
			list = append(list, filepath.Join(local_folder, f.Name()))
		}
	}

	return list
}
<commit_msg>Well, now it should work<commit_after>package main

import (
	"fmt"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/net/context"
	"google.golang.org/api/drive/v2"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var clearline string = fmt.Sprintf("%c[2K\r", 27)
var current_file string

func sizeToString(size int64) string {
	prefix := ""
	flsz := float64(0)

	if size > 1<<30 {
		prefix = "G"
		flsz = float64(size) / float64(1<<30)
	} else if size > 1<<20 {
		prefix = "M"
		flsz = float64(size) / float64(1<<20)
	} else if size > 1<<10 {
		prefix = "k"
		flsz = float64(size) / float64(1<<10)
	} else {
		flsz = float64(size)
	}

	return fmt.Sprintf("%.1f%s", flsz, prefix)
}

func getProgressFunction(filename string) func(done, max int64) {
	return func(done, max int64) {
		if current_file == "" {
			current_file = filename
		}
		if filename != current_file {
			current_file = filename
			fmt.Println()
		}

		width, _, _ := terminal.GetSize(0)
		bar_width := width - 33

		n_filled := int((float64(done) / float64(max)) * float64(bar_width))

		sizestr := sizeToString(done)

		if n_filled > len(sizestr) {
			n_filled -= len(sizestr)
		} else {
			n_filled = 0
		}

		n_empty := bar_width - n_filled - len(sizestr)

		filled := strings.Repeat("=", n_filled) + sizestr
		empty := strings.Repeat(" ", n_empty)

		short_filename := filename

		if len(filename) > 30 {
			short_filename = "..." + filename[len(filename)-27:len(filename)]
		}

		bar := fmt.Sprintf("%30s [%s%s]", short_filename, filled, empty)

		fmt.Print(clearline)
		fmt.Print(bar)
	}
}

func uploadFileList(cl *drive.Service, list []string) error {
	pathtoidmap := make(map[string]string)
	pathtoidmap["."] = FLAG_folder_id

	for _, file := range list {
		dir := filepath.Dir(file)

		id, ok := pathtoidmap[dir]

		if !ok {
			err := createNestedFolders(cl, pathtoidmap, FLAG_folder_id, strings.Split(dir, "/"))
			if err != nil {
				return err
			}
			id = pathtoidmap[dir]
		}

		parentref := &drive.ParentReference{Id: id}
		drive_file := &drive.File{Title: filepath.Base(file), Parents: []*drive.ParentReference{parentref}}

		data, err := os.Open(file)

		if err != nil {
			log.Print(err.Error())
			continue
		}

		stat, err := data.Stat()

		_, err = cl.Files.Insert(drive_file).
			ResumableMedia(context.Background(), data, stat.Size(), "").
			ProgressUpdater(getProgressFunction(file)).
			Do()

		data.Close()

		if err != nil {
			log.Print(err.Error())
		}
	}
	fmt.Println("")

	return nil
}

func createNestedFolders(cl *drive.Service, pathtoid map[string]string, root string, folders []string) error {
	parent := root
	path := ""

	for _, folder := range folders {
		if folder == "" || folder == "." {
			continue
		}

		path = filepath.Join(path, folder)

		next_parent, ok := pathtoid[path]

		if ok {
			parent = next_parent
			continue
		} else {

			query := "title = '" + folder + "' and '" + parent + "' in parents"

			list, err := cl.Files.List().Q(query).Do()

			if err != nil {
				return err
			}

			if len(list.Items) == 0 {
				parentref := &drive.ParentReference{Id: parent}
				file := &drive.File{MimeType: "application/vnd.google-apps.folder",
					Title: folder, Parents: []*drive.ParentReference{parentref}}

				f, err := cl.Files.Insert(file).Do()

				if err != nil {
					return err
				}
				pathtoid[path] = f.Id
				parent = f.Id
			} else {
				pathtoid[path] = list.Items[0].Id
				parent = list.Items[0].Id
			}
		}
	}

	return nil
}

func getFileList(local_folder string) []string {
	list := make([]string, 0, 16)

	st, err := os.Stat(local_folder)

	if err != nil {
		log.Fatal(err)
		return list
	}

	if !st.IsDir() {
		return append(list, local_folder)
	}

	files, err := ioutil.ReadDir(local_folder)

	if err != nil {
		log.Fatal(err)
		return list
	}

	for _, f := range files {
		if f.IsDir() {
			list = append(list, getFileList(filepath.Join(local_folder, f.Name()))...)
		} else {
			list = append(list, filepath.Join(local_folder, f.Name()))
		}
	}

	return list
}
<|endoftext|>


<commit_before>// Copyright 2016 Stratumn SAS. All rights reserved.
// Use of this source code is governed by an Apache License 2.0
// that can be found in the LICENSE file.

// Package bcbatchfossilizer implements a fossilizer that fossilize batches of hashes on a blockchain.
package bcbatchfossilizer

import (
	"fmt"

	log "github.com/Sirupsen/logrus"

	"github.com/stratumn/go/fossilizer"
	"github.com/stratumn/go/types"

	"github.com/stratumn/goprivate/batchfossilizer"
	"github.com/stratumn/goprivate/blockchain"
)

const (
	// Name is the name set in the fossilizer's information.
	Name = "bcbatch"

	// Description is the description set in the fossilizer's information.
	Description = "Stratumn Blockchain Batch Fossilizer"
)

// Config contains configuration options for the fossilizer.
type Config struct {
	HashTimestamper blockchain.HashTimestamper
}

// Info is the info returned by GetInfo.
type Info struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Version     string `json:"version"`
	Commit      string `json:"commit"`
	Blockchain  string `json:"blockchain"`
}

// Evidence is the evidence sent to the result channel.
type Evidence struct {
	*batchfossilizer.Evidence
	TransactionID blockchain.TransactionID `json:"txid"`
}

// Fossilizer is the type that implements github.com/stratumn/go/fossilizer.Adapter.
type Fossilizer struct {
	*batchfossilizer.Fossilizer
	config            *Config
	lastRoot          *types.Bytes32
	lastTransactionID blockchain.TransactionID
}

// New creates an instance of a Fossilizer.
func New(config *Config, batchConfig *batchfossilizer.Config) (*Fossilizer, error) {
	if batchConfig.MaxSimBatches > 1 {
		return nil, fmt.Errorf("MaxSimBatches is %d want less than 2", batchConfig.MaxSimBatches)
	}

	b, err := batchfossilizer.New(batchConfig)
	if err != nil {
		return nil, err
	}

	f := Fossilizer{
		Fossilizer: b,
		config:     config,
	}

	f.SetTransformer(f.transform)

	return &f, err
}

// GetInfo implements github.com/stratumn/go/fossilizer.Adapter.GetInfo.
func (a *Fossilizer) GetInfo() (interface{}, error) {
	batchInfo, err := a.Fossilizer.GetInfo()
	if err != nil {
		return nil, err
	}

	info, ok := batchInfo.(*batchfossilizer.Info)
	if !ok {
		return nil, fmt.Errorf("unexpected batchfossilizer info %#v", batchInfo)
	}

	return &Info{
		Name:        Name,
		Description: Description,
		Version:     info.Version,
		Commit:      info.Commit,
		Blockchain:  a.config.HashTimestamper.Network().String(),
	}, nil
}

func (a *Fossilizer) transform(evidence *batchfossilizer.Evidence, data, meta []byte) (*fossilizer.Result, error) {
	var (
		root = evidence.Root
		txid blockchain.TransactionID
		err  error
	)

	if a.lastRoot == nil || *root != *a.lastRoot {
		txid, err = a.config.HashTimestamper.TimestampHash(root)
		if err != nil {
			return nil, err
		}
		log.WithFields(log.Fields{
			"txid": txid,
			"root": root,
		}).Info("Broadcasted transaction")

		a.lastRoot = root
		a.lastTransactionID = txid
	}

	evidenceWrapper := map[string]*Evidence{}
	evidenceWrapper[a.config.HashTimestamper.Network().String()] = &Evidence{
		Evidence:      evidence,
		TransactionID: a.lastTransactionID,
	}

	r := fossilizer.Result{
		Evidence: evidenceWrapper,
		Data:     data,
		Meta:     meta,
	}

	return &r, nil
}
<commit_msg>bcbatchfossilizer: Sirupsen -> sirupsen<commit_after>// Copyright 2016 Stratumn SAS. All rights reserved.
// Use of this source code is governed by an Apache License 2.0
// that can be found in the LICENSE file.

// Package bcbatchfossilizer implements a fossilizer that fossilize batches of hashes on a blockchain.
package bcbatchfossilizer

import (
	"fmt"

	log "github.com/sirupsen/logrus"

	"github.com/stratumn/go/fossilizer"
	"github.com/stratumn/go/types"

	"github.com/stratumn/goprivate/batchfossilizer"
	"github.com/stratumn/goprivate/blockchain"
)

const (
	// Name is the name set in the fossilizer's information.
	Name = "bcbatch"

	// Description is the description set in the fossilizer's information.
	Description = "Stratumn Blockchain Batch Fossilizer"
)

// Config contains configuration options for the fossilizer.
type Config struct {
	HashTimestamper blockchain.HashTimestamper
}

// Info is the info returned by GetInfo.
type Info struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Version     string `json:"version"`
	Commit      string `json:"commit"`
	Blockchain  string `json:"blockchain"`
}

// Evidence is the evidence sent to the result channel.
type Evidence struct {
	*batchfossilizer.Evidence
	TransactionID blockchain.TransactionID `json:"txid"`
}

// Fossilizer is the type that implements github.com/stratumn/go/fossilizer.Adapter.
type Fossilizer struct {
	*batchfossilizer.Fossilizer
	config            *Config
	lastRoot          *types.Bytes32
	lastTransactionID blockchain.TransactionID
}

// New creates an instance of a Fossilizer.
func New(config *Config, batchConfig *batchfossilizer.Config) (*Fossilizer, error) {
	if batchConfig.MaxSimBatches > 1 {
		return nil, fmt.Errorf("MaxSimBatches is %d want less than 2", batchConfig.MaxSimBatches)
	}

	b, err := batchfossilizer.New(batchConfig)
	if err != nil {
		return nil, err
	}

	f := Fossilizer{
		Fossilizer: b,
		config:     config,
	}

	f.SetTransformer(f.transform)

	return &f, err
}

// GetInfo implements github.com/stratumn/go/fossilizer.Adapter.GetInfo.
func (a *Fossilizer) GetInfo() (interface{}, error) {
	batchInfo, err := a.Fossilizer.GetInfo()
	if err != nil {
		return nil, err
	}

	info, ok := batchInfo.(*batchfossilizer.Info)
	if !ok {
		return nil, fmt.Errorf("unexpected batchfossilizer info %#v", batchInfo)
	}

	return &Info{
		Name:        Name,
		Description: Description,
		Version:     info.Version,
		Commit:      info.Commit,
		Blockchain:  a.config.HashTimestamper.Network().String(),
	}, nil
}

func (a *Fossilizer) transform(evidence *batchfossilizer.Evidence, data, meta []byte) (*fossilizer.Result, error) {
	var (
		root = evidence.Root
		txid blockchain.TransactionID
		err  error
	)

	if a.lastRoot == nil || *root != *a.lastRoot {
		txid, err = a.config.HashTimestamper.TimestampHash(root)
		if err != nil {
			return nil, err
		}
		log.WithFields(log.Fields{
			"txid": txid,
			"root": root,
		}).Info("Broadcasted transaction")

		a.lastRoot = root
		a.lastTransactionID = txid
	}

	evidenceWrapper := map[string]*Evidence{}
	evidenceWrapper[a.config.HashTimestamper.Network().String()] = &Evidence{
		Evidence:      evidence,
		TransactionID: a.lastTransactionID,
	}

	r := fossilizer.Result{
		Evidence: evidenceWrapper,
		Data:     data,
		Meta:     meta,
	}

	return &r, nil
}
<|endoftext|>


<commit_before>package main

import (
	// Stdlib
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	// Internal
	"github.com/salsita/salsaflow/git"
	"github.com/salsita/salsaflow/log"
	"github.com/salsita/salsaflow/uuid"
)

const (
	stateContent = iota + 1
	stateTags
)

var (
	changeIdPattern = regexp.MustCompile("^(?i)Change-Id:")
	storyIdPattern  = regexp.MustCompile("^(?i)Story-Id:")
)

const diffSeparator = "# ------------------------ >8 ------------------------"

const (
	secretFilename = "AreYouWhoIthinkYouAreHuh"
	secretReply    = "IAmGitFlowWebhookYaDoofus!"
)

func main() {
	if len(os.Args) != 2 {
		panic(fmt.Errorf("argv: %#v", os.Args))
	}

	if os.Args[1] == secretFilename {
		fmt.Println(secretReply)
		return
	}

	if err := run(os.Args[1]); err != nil {
		log.Fatalln(err)
	}
}

func run(msgPath string) error {
	// Open the commit message file.
	file, err := os.OpenFile(msgPath, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	// Read and parse the commit message.
	var (
		state        = stateContent
		changeIdSeen bool
		storyIdSeen  bool
		lines        []string
	)
	scanner := bufio.NewScanner(file)
ScanLoop:
	for scanner.Scan() {
		line := scanner.Text()
		trimmedLine := strings.TrimSpace(line)

		// Keep appending content until a tag is encountered.
		if state == stateContent {
			switch {
			case changeIdPattern.MatchString(trimmedLine):
				if changeIdSeen {
					return errors.New("multiple Change-Id tags detected")
				}
				changeIdSeen = true
				state = stateTags

			case storyIdPattern.MatchString(trimmedLine):
				if storyIdSeen {
					return errors.New("multiple Story-Id tags detected")
				}
				storyIdSeen = true
				state = stateTags
			}

			lines = append(lines, line)
			continue ScanLoop
		}

		// In case Change-Id or Story-Id is already there, consume the rest of the input
		// in a similar way as the previous state, just drop empty lines and comments.
		if state == stateTags {
			if trimmedLine == "" {
				continue
			}

			switch {
			case changeIdPattern.MatchString(trimmedLine):
				if changeIdSeen {
					return errors.New("multiple Change-Id tags detected")
				}
				changeIdSeen = true

			case storyIdPattern.MatchString(trimmedLine):
				if storyIdSeen {
					return errors.New("multiple Story-Id tags detected")
				}
				storyIdSeen = true

			case line == diffSeparator:
				// Everything below diffSeparator is anyway removed by git,
				// so we can as well just skip it and drop the content here.
				break ScanLoop

			case strings.HasPrefix(line, "#"):
				// Let's drop comments here.
				// This must come after the previous case, otherwise
				// the diff separator is dropped as well.
				continue ScanLoop
			}

			lines = append(lines, line)
			continue ScanLoop
		}

		panic("unreachable code reached")
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	// Do nothing in case the file is empty.
	if len(lines) == 0 {
		return nil
	}

	// Return if all the tags are already there.
	if changeIdSeen && storyIdSeen {
		return nil
	}

	// Append a newline if there are no tags yet.
	if state != stateTags && lines[len(lines)-1] != "\n" {
		lines = append(lines, "")
	}

	// Get the values for the missing tags.
	if !changeIdSeen {
		changeId, err := uuid.New()
		if err != nil {
			return err
		}
		lines = append(lines, fmt.Sprintf("Change-Id: %v", changeId))
	}

	if !storyIdSeen {
		branch, stderr, err := git.CurrentBranch()
		if err != nil {
			log.Println(stderr)
			return err
		}

		matcher := regexp.MustCompile("^story/.+/([0-9]+)$")
		parts := matcher.FindStringSubmatch(branch)
		if len(parts) == 2 {
			lines = append(lines, fmt.Sprintf("Story-Id: %v", parts[1]))
		}
	}

	// Write the content back to the disk (truncate the file first).
	_, err = file.Seek(0, os.SEEK_SET)
	if err != nil {
		return err
	}
	if err := file.Truncate(0); err != nil {
		return err
	}

	content := strings.Join(lines, "\n") + "\n"
	_, err = io.Copy(file, strings.NewReader(content))
	return err
}
<commit_msg>hooks/commit-msg: Fix story branch matching<commit_after>package main

import (
	// Stdlib
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	// Internal
	"github.com/salsita/salsaflow/git"
	"github.com/salsita/salsaflow/log"
	"github.com/salsita/salsaflow/uuid"
)

const (
	stateContent = iota + 1
	stateTags
)

var (
	changeIdPattern = regexp.MustCompile("^(?i)Change-Id:")
	storyIdPattern  = regexp.MustCompile("^(?i)Story-Id:")
)

const diffSeparator = "# ------------------------ >8 ------------------------"

const (
	secretFilename = "AreYouWhoIthinkYouAreHuh"
	secretReply    = "IAmGitFlowWebhookYaDoofus!"
)

func main() {
	if len(os.Args) != 2 {
		panic(fmt.Errorf("argv: %#v", os.Args))
	}

	if os.Args[1] == secretFilename {
		fmt.Println(secretReply)
		return
	}

	if err := run(os.Args[1]); err != nil {
		log.Fatalln(err)
	}
}

func run(msgPath string) error {
	// Open the commit message file.
	file, err := os.OpenFile(msgPath, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	// Read and parse the commit message.
	var (
		state        = stateContent
		changeIdSeen bool
		storyIdSeen  bool
		lines        []string
	)
	scanner := bufio.NewScanner(file)
ScanLoop:
	for scanner.Scan() {
		line := scanner.Text()
		trimmedLine := strings.TrimSpace(line)

		// Keep appending content until a tag is encountered.
		if state == stateContent {
			switch {
			case changeIdPattern.MatchString(trimmedLine):
				if changeIdSeen {
					return errors.New("multiple Change-Id tags detected")
				}
				changeIdSeen = true
				state = stateTags

			case storyIdPattern.MatchString(trimmedLine):
				if storyIdSeen {
					return errors.New("multiple Story-Id tags detected")
				}
				storyIdSeen = true
				state = stateTags
			}

			lines = append(lines, line)
			continue ScanLoop
		}

		// In case Change-Id or Story-Id is already there, consume the rest of the input
		// in a similar way as the previous state, just drop empty lines and comments.
		if state == stateTags {
			if trimmedLine == "" {
				continue
			}

			switch {
			case changeIdPattern.MatchString(trimmedLine):
				if changeIdSeen {
					return errors.New("multiple Change-Id tags detected")
				}
				changeIdSeen = true

			case storyIdPattern.MatchString(trimmedLine):
				if storyIdSeen {
					return errors.New("multiple Story-Id tags detected")
				}
				storyIdSeen = true

			case line == diffSeparator:
				// Everything below diffSeparator is anyway removed by git,
				// so we can as well just skip it and drop the content here.
				break ScanLoop

			case strings.HasPrefix(line, "#"):
				// Let's drop comments here.
				// This must come after the previous case, otherwise
				// the diff separator is dropped as well.
				continue ScanLoop
			}

			lines = append(lines, line)
			continue ScanLoop
		}

		panic("unreachable code reached")
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	// Do nothing in case the file is empty.
	if len(lines) == 0 {
		return nil
	}

	// Return if all the tags are already there.
	if changeIdSeen && storyIdSeen {
		return nil
	}

	// Append a newline if there are no tags yet.
	if state != stateTags && lines[len(lines)-1] != "\n" {
		lines = append(lines, "")
	}

	// Get the values for the missing tags.
	if !changeIdSeen {
		changeId, err := uuid.New()
		if err != nil {
			return err
		}
		lines = append(lines, fmt.Sprintf("Change-Id: %v", changeId))
	}

	if !storyIdSeen {
		branch, stderr, err := git.CurrentBranch()
		if err != nil {
			log.Println(stderr)
			return err
		}

		storyId, err := git.RefToStoryId(branch)
		if err == nil {
			lines = append(lines, fmt.Sprintf("Story-Id: %v", storyId))
		}
	}

	// Write the content back to the disk (truncate the file first).
	_, err = file.Seek(0, os.SEEK_SET)
	if err != nil {
		return err
	}
	if err := file.Truncate(0); err != nil {
		return err
	}

	content := strings.Join(lines, "\n") + "\n"
	_, err = io.Copy(file, strings.NewReader(content))
	return err
}
<|endoftext|>


<commit_before>package main

import "fmt"

func seriesTypeToText(id int) string {
	seriesTypes := []string{"bo1", "bo3", "bo5"}
	return seriesTypes[id]
}

func makeMatchText(matchData oDotaMatchData) string {
	radiantName := matchData.RadiantTeam.Name
	direName := matchData.DireTeam.Name
	radiantScore := matchData.RadiantScore
	direScore := matchData.DireScore
	hours, minutes, seconds := secondsToTime(matchData.Duration)
	leagueName := matchData.League.Name
	seriesID := matchData.SeriesID
	seriesType := matchData.SeriesType

	if radiantName == "" {
		radiantName = "Radiant"
	}
	if direName == "" {
		direName = "Dire"
	}

	seriesText := seriesTypeToText(seriesType)

	if matchData.RadiantWin == true {
		if seriesID != 0 {
			return fmt.Sprintf("🏆 [rsltdtk|%s] %d - %d %s\n%d:%02d:%02d @ %s, %s\n#rsltdtk #dota2 #results@rsltdtk #s%d@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName, seriesText, seriesID)
		} else {
			return fmt.Sprintf("🏆 [rsltdtk|%s] %d - %d %s\n%d:%02d:%02d @ %s\n#rsltdtk #dota2 #results@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName)
		}
	} else {
		if seriesID != 0 {
			return fmt.Sprintf("%s %d - %d 🏆 [rsltdtk|%s]\n%d:%02d:%02d @ %s, %s\n#rsltdtk #dota2 #results@rsltdtk #s%d@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName, seriesText, seriesID)
		} else {
			return fmt.Sprintf("%s %d - %d 🏆 [rsltdtk|%s]\n%d:%02d:%02d @ %s\n#rsltdtk #dota2 #results@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName)
		}
	}
}
<commit_msg>Fix text in text<commit_after>package main

import "fmt"

func seriesTypeToText(id int) string {
	seriesTypes := []string{"bo1", "bo3", "bo5"}
	return seriesTypes[id]
}

func makeMatchText(matchData oDotaMatchData) string {
	radiantName := matchData.RadiantTeam.Name
	direName := matchData.DireTeam.Name
	radiantScore := matchData.RadiantScore
	direScore := matchData.DireScore
	hours, minutes, seconds := secondsToTime(matchData.Duration)
	leagueName := matchData.League.Name
	seriesID := matchData.SeriesID
	seriesType := matchData.SeriesType

	if radiantName == "" {
		radiantName = matchData.RadiantName
	}
	if direName == "" {
		direName = matchData.DireName
	}

	if radiantName == "" {
		radiantName = "Radiant"
	}
	if direName == "" {
		direName = "Dire"
	}

	if leagueName == "" {
		leaguesData := getLeaguesData()
		leagueName = getLeagueName(matchData.Leagueid, leaguesData)
	}

	seriesText := seriesTypeToText(seriesType)

	if matchData.RadiantWin == true {
		if seriesID != 0 {
			return fmt.Sprintf("🏆 [rsltdtk|%s] %d - %d %s\n%d:%02d:%02d @ %s, %s\n#rsltdtk #dota2 #results@rsltdtk #s%d@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName, seriesText, seriesID)
		} else {
			return fmt.Sprintf("🏆 [rsltdtk|%s] %d - %d %s\n%d:%02d:%02d @ %s\n#rsltdtk #dota2 #results@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName)
		}
	} else {
		if seriesID != 0 {
			return fmt.Sprintf("%s %d - %d 🏆 [rsltdtk|%s]\n%d:%02d:%02d @ %s, %s\n#rsltdtk #dota2 #results@rsltdtk #s%d@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName, seriesText, seriesID)
		} else {
			return fmt.Sprintf("%s %d - %d 🏆 [rsltdtk|%s]\n%d:%02d:%02d @ %s\n#rsltdtk #dota2 #results@rsltdtk",
				radiantName, radiantScore, direScore, direName, hours, minutes, seconds, leagueName)
		}
	}
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 Shlomi Noach.

   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 math

func MinInt(i1, i2 int) int {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxInt(i1, i2 int) int {
	if i1 > i2 {
		return i1
	}
	return i2
}

func MinInt64(i1, i2 int64) int64 {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxInt64(i1, i2 int64) int64 {
	if i1 > i2 {
		return i1
	}
	return i2
}

func MinUInt(i1, i2 uint) uint {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxUInt(i1, i2 uint) uint {
	if i1 > i2 {
		return i1
	}
	return i2
}

func MinUInt64(i1, i2 uint64) uint64 {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxUInt64(i1, i2 uint64) uint64 {
	if i1 > i2 {
		return i1
	}
	return i2
}

// TernaryString acts as liek an "? :" C-style ternary operator for strings
func TernaryString(condition bool, resTrue string, resFalse string) string {
	if condition {
		return resTrue
	}
	return resFalse
}

// TernaryString acts as liek an "? :" C-style ternary operator for ints
func TernaryInt(condition bool, resTrue int, resFalse int) int {
	if condition {
		return resTrue
	}
	return resFalse
}

// AbsInt is an ABS function for int type
func AbsInt(i int) int {
	if i >= 0 {
		return i
	}
	return -i
}

// AbsInt64 is an ABS function for int64 type
func AbsInt64(i int64) int64 {
	if i >= 0 {
		return i
	}
	return -i
}
<commit_msg>minor doc typo fixed<commit_after>/*
   Copyright 2014 Shlomi Noach.

   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 math

func MinInt(i1, i2 int) int {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxInt(i1, i2 int) int {
	if i1 > i2 {
		return i1
	}
	return i2
}

func MinInt64(i1, i2 int64) int64 {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxInt64(i1, i2 int64) int64 {
	if i1 > i2 {
		return i1
	}
	return i2
}

func MinUInt(i1, i2 uint) uint {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxUInt(i1, i2 uint) uint {
	if i1 > i2 {
		return i1
	}
	return i2
}

func MinUInt64(i1, i2 uint64) uint64 {
	if i1 < i2 {
		return i1
	}
	return i2
}

func MaxUInt64(i1, i2 uint64) uint64 {
	if i1 > i2 {
		return i1
	}
	return i2
}

// TernaryString acts like a "? :" C-style ternary operator for strings
func TernaryString(condition bool, resTrue string, resFalse string) string {
	if condition {
		return resTrue
	}
	return resFalse
}

// TernaryString acts like a "? :" C-style ternary operator for ints
func TernaryInt(condition bool, resTrue int, resFalse int) int {
	if condition {
		return resTrue
	}
	return resFalse
}

// AbsInt is an ABS function for int type
func AbsInt(i int) int {
	if i >= 0 {
		return i
	}
	return -i
}

// AbsInt64 is an ABS function for int64 type
func AbsInt64(i int64) int64 {
	if i >= 0 {
		return i
	}
	return -i
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	jsonpb "github.com/gogo/protobuf/jsonpb"
	ggproto "github.com/gogo/protobuf/proto"
	gopass "github.com/howeyc/gopass"
	b58 "github.com/jbenet/go-base58"
	p2p_crypto "github.com/libp2p/go-libp2p-crypto"
	mc "github.com/mediachain/concat/mc"
	pb "github.com/mediachain/concat/proto"
	homedir "github.com/mitchellh/go-homedir"
	sbox "golang.org/x/crypto/nacl/secretbox"
	scrypt "golang.org/x/crypto/scrypt"
	kp "gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"
)

func main() {
	log.SetFlags(0) // naked logs, it's interactive output

	var (
		home = kp.Flag("home", "mcid home directory").Short('d').Default("~/.mediachain/mcid").String()

		_ = kp.Command("id", "show your public identity; generates a new key pair if it doesn't already exist.") // idCmd declared but not used

		signCmd      = kp.Command("sign", "sign a manifest")
		signEntity   = signCmd.Arg("entity", "entity id").Required().String()
		signManifest = signCmd.Arg("manifest", "manifest json file").Required().File()

		verifyCmd      = kp.Command("verify", "verify a manifest")
		verifyManifest = verifyCmd.Arg("manifest", "manifest json file").Required().ExistingFile()
	)

	switch kp.Parse() {
	case "id":
		doId(*home)

	case "sign":
		doSign(*home, *signEntity, *signManifest)

	case "verify":
		doVerify(*home, *verifyManifest)
	}
}

type Identity struct {
	Public  PublicId  `json:"public"`
	Private PrivateId `json:"private"`
}

type PublicId struct {
	KeyId string `json:"keyId"` // public key multihash
	Key   []byte `json:"key"`   // marshalled public key
}

type PrivateId struct {
	Params ScryptParams `json:"params"` // key derivation parameters
	Salt   []byte       `json:"salt"`   // key derivation salt
	Nonce  []byte       `json:"nonce"`  // encryption nonce
	Data   []byte       `json:"data"`   // encrypted marshalled private key
}

type ScryptParams struct {
	N, R, P int
}

// default scrypt parameters (2009): N=16384, r=8, p=1
// still current; see https://github.com/Tarsnap/scrypt/issues/19
const (
	ScryptN = 16384
	ScryptR = 8
	ScryptP = 1
)

var (
	DecryptionError = errors.New("Private key decryption failed")
)

// ops
func doId(home string) {
	id, err := getIdentity(home, true) // generate id if it doesn't already exist
	if err != nil {
		log.Fatalf("Error retrieving identity: %s", err.Error())
	}

	json.NewEncoder(os.Stdout).Encode(id.Public)
}

func doSign(home string, entity string, mf *os.File) {
	var manifest pb.Manifest
	var manifestBody pb.ManifestBody

	err := jsonpb.Unmarshal(mf, &manifestBody)
	if err != nil {
		log.Fatalf("Error decoding manifest body: %s", err.Error())
	}

	id, err := getIdentity(home, false) // error if id doesn't exist
	if err != nil {
		log.Fatalf("Error retrieving identity: %s", err.Error())
	}

	privk, err := getPrivateKey(id.Private)
	if err != nil {
		log.Fatalf("Error decrypting private key: %s", err.Error())
	}

	manifest.Entity = entity
	manifest.KeyId = id.Public.KeyId
	manifest.Body = &manifestBody
	manifest.Timestamp = time.Now().Unix()

	bytes, err := ggproto.Marshal(&manifest)
	if err != nil {
		log.Fatalf("Error marshalling manifest: %s", err.Error())
	}

	sig, err := privk.Sign(bytes)
	if err != nil {
		log.Fatalf("Error signing manifest: %s", err.Error())
	}

	manifest.Signature = sig

	marshaler := jsonpb.Marshaler{}
	err = marshaler.Marshal(os.Stdout, &manifest)
	if err != nil {
		log.Fatalf("Error encoding manifest: %s", err.Error())
	}
	fmt.Println("")
}

func doVerify(home string, manifest string) {
	log.Fatalf("IMPLEMENT ME: doVerify %s %s\n", home, manifest)
}

// identity
func getIdentity(home string, genid bool) (id Identity, err error) {
	home, err = homedir.Expand(home)
	if err != nil {
		return
	}

	idpath := path.Join(home, "identity.json")
	_, err = os.Stat(idpath)
	switch {
	case os.IsNotExist(err):
		if genid {
			return generateIdentity(home, idpath)
		}
		fallthrough
	case err != nil:
		return

	default:
		return loadIdentity(idpath)
	}
}

func generateIdentity(home, idpath string) (id Identity, err error) {
	err = os.MkdirAll(home, 0755)
	if err != nil {
		return
	}

	log.Printf("Generating identity key pair")
	privk, pubk, err := mc.GenerateECCKeyPair()
	if err != nil {
		return
	}

	pubbytes, err := pubk.Bytes()
	if err != nil {
		return
	}
	kid := b58.Encode(mc.Hash(pubbytes))

	id.Public.KeyId = kid
	id.Public.Key = pubbytes

	privbytes, err := privk.Bytes()
	if err != nil {
		return
	}

	err = encryptPrivateId(&id.Private, privbytes)
	if err != nil {
		return
	}

	bytes, err := json.Marshal(&id)
	if err != nil {
		return
	}

	err = ioutil.WriteFile(idpath, bytes, 0600)
	return
}

func loadIdentity(idpath string) (id Identity, err error) {
	bytes, err := ioutil.ReadFile(idpath)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &id)
	return
}

func getPrivateKey(priv PrivateId) (p2p_crypto.PrivKey, error) {
	bytes, err := decryptPrivateId(priv)
	if err != nil {
		return nil, err
	}

	return p2p_crypto.UnmarshalPrivateKey(bytes)
}

// private key encryption/decryption:
//  key derivation with scrypt
//  encryption with nacl secretbox
func encryptPrivateId(priv *PrivateId, data []byte) error {
	var (
		salt  [16]byte
		nonce [24]byte
		key   [32]byte
	)

	_, err := rand.Read(salt[:])
	if err != nil {
		return err
	}

	_, err = rand.Read(nonce[:])
	if err != nil {
		return err
	}

	pass, err := getEncryptionPass()
	if err != nil {
		return err
	}

	xkey, err := scrypt.Key(pass, salt[:], ScryptN, ScryptR, ScryptP, 32)
	if err != nil {
		return err
	}

	copy(key[:], xkey)

	ctext := sbox.Seal(nil, data, &nonce, &key)

	priv.Salt = salt[:]
	priv.Nonce = nonce[:]
	priv.Data = ctext[:]
	priv.Params.N = ScryptN
	priv.Params.R = ScryptR
	priv.Params.P = ScryptP

	return nil
}

func decryptPrivateId(priv PrivateId) ([]byte, error) {
	var (
		nonce [24]byte
		key   [32]byte
	)

	pass, err := getDecryptionPass()
	if err != nil {
		return nil, err
	}

	xkey, err := scrypt.Key(pass, priv.Salt, priv.Params.N, priv.Params.R, priv.Params.P, 32)
	if err != nil {
		return nil, err
	}

	copy(nonce[:], priv.Nonce)
	copy(key[:], xkey)

	bytes, ok := sbox.Open(nil, priv.Data, &nonce, &key)
	if !ok {
		return nil, DecryptionError
	}

	return bytes, nil
}

func getEncryptionPass() ([]byte, error) {
	for {
		fmt.Printf("Enter passphrase: ")
		pass1, err := gopass.GetPasswd()
		if err != nil {
			return nil, err
		}

		fmt.Printf("Re-enter passphrase: ")
		pass2, err := gopass.GetPasswd()
		if err != nil {
			return nil, err
		}

		if bytes.Equal(pass1, pass2) {
			return pass1, nil
		}

		fmt.Println("Passphrases don't match")
	}
}

func getDecryptionPass() ([]byte, error) {
	fmt.Printf("Enter passphrase: ")
	return gopass.GetPasswd()
}
<commit_msg>mcid: request passphrase through stderr<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	jsonpb "github.com/gogo/protobuf/jsonpb"
	ggproto "github.com/gogo/protobuf/proto"
	gopass "github.com/howeyc/gopass"
	b58 "github.com/jbenet/go-base58"
	p2p_crypto "github.com/libp2p/go-libp2p-crypto"
	mc "github.com/mediachain/concat/mc"
	pb "github.com/mediachain/concat/proto"
	homedir "github.com/mitchellh/go-homedir"
	sbox "golang.org/x/crypto/nacl/secretbox"
	scrypt "golang.org/x/crypto/scrypt"
	kp "gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"
)

func main() {
	log.SetFlags(0) // naked logs, it's interactive output

	var (
		home = kp.Flag("home", "mcid home directory").Short('d').Default("~/.mediachain/mcid").String()

		_ = kp.Command("id", "show your public identity; generates a new key pair if it doesn't already exist.") // idCmd declared but not used

		signCmd      = kp.Command("sign", "sign a manifest")
		signEntity   = signCmd.Arg("entity", "entity id").Required().String()
		signManifest = signCmd.Arg("manifest", "manifest json file").Required().File()

		verifyCmd      = kp.Command("verify", "verify a manifest")
		verifyManifest = verifyCmd.Arg("manifest", "manifest json file").Required().ExistingFile()
	)

	switch kp.Parse() {
	case "id":
		doId(*home)

	case "sign":
		doSign(*home, *signEntity, *signManifest)

	case "verify":
		doVerify(*home, *verifyManifest)
	}
}

type Identity struct {
	Public  PublicId  `json:"public"`
	Private PrivateId `json:"private"`
}

type PublicId struct {
	KeyId string `json:"keyId"` // public key multihash
	Key   []byte `json:"key"`   // marshalled public key
}

type PrivateId struct {
	Params ScryptParams `json:"params"` // key derivation parameters
	Salt   []byte       `json:"salt"`   // key derivation salt
	Nonce  []byte       `json:"nonce"`  // encryption nonce
	Data   []byte       `json:"data"`   // encrypted marshalled private key
}

type ScryptParams struct {
	N, R, P int
}

// default scrypt parameters (2009): N=16384, r=8, p=1
// still current; see https://github.com/Tarsnap/scrypt/issues/19
const (
	ScryptN = 16384
	ScryptR = 8
	ScryptP = 1
)

var (
	DecryptionError = errors.New("Private key decryption failed")
)

// ops
func doId(home string) {
	id, err := getIdentity(home, true) // generate id if it doesn't already exist
	if err != nil {
		log.Fatalf("Error retrieving identity: %s", err.Error())
	}

	json.NewEncoder(os.Stdout).Encode(id.Public)
}

func doSign(home string, entity string, mf *os.File) {
	var manifest pb.Manifest
	var manifestBody pb.ManifestBody

	err := jsonpb.Unmarshal(mf, &manifestBody)
	if err != nil {
		log.Fatalf("Error decoding manifest body: %s", err.Error())
	}

	id, err := getIdentity(home, false) // error if id doesn't exist
	if err != nil {
		log.Fatalf("Error retrieving identity: %s", err.Error())
	}

	privk, err := getPrivateKey(id.Private)
	if err != nil {
		log.Fatalf("Error decrypting private key: %s", err.Error())
	}

	manifest.Entity = entity
	manifest.KeyId = id.Public.KeyId
	manifest.Body = &manifestBody
	manifest.Timestamp = time.Now().Unix()

	bytes, err := ggproto.Marshal(&manifest)
	if err != nil {
		log.Fatalf("Error marshalling manifest: %s", err.Error())
	}

	sig, err := privk.Sign(bytes)
	if err != nil {
		log.Fatalf("Error signing manifest: %s", err.Error())
	}

	manifest.Signature = sig

	marshaler := jsonpb.Marshaler{}
	err = marshaler.Marshal(os.Stdout, &manifest)
	if err != nil {
		log.Fatalf("Error encoding manifest: %s", err.Error())
	}
	fmt.Println("")
}

func doVerify(home string, manifest string) {
	log.Fatalf("IMPLEMENT ME: doVerify %s %s\n", home, manifest)
}

// identity
func getIdentity(home string, genid bool) (id Identity, err error) {
	home, err = homedir.Expand(home)
	if err != nil {
		return
	}

	idpath := path.Join(home, "identity.json")
	_, err = os.Stat(idpath)
	switch {
	case os.IsNotExist(err):
		if genid {
			return generateIdentity(home, idpath)
		}
		fallthrough
	case err != nil:
		return

	default:
		return loadIdentity(idpath)
	}
}

func generateIdentity(home, idpath string) (id Identity, err error) {
	err = os.MkdirAll(home, 0755)
	if err != nil {
		return
	}

	log.Printf("Generating identity key pair")
	privk, pubk, err := mc.GenerateECCKeyPair()
	if err != nil {
		return
	}

	pubbytes, err := pubk.Bytes()
	if err != nil {
		return
	}
	kid := b58.Encode(mc.Hash(pubbytes))

	id.Public.KeyId = kid
	id.Public.Key = pubbytes

	privbytes, err := privk.Bytes()
	if err != nil {
		return
	}

	err = encryptPrivateId(&id.Private, privbytes)
	if err != nil {
		return
	}

	bytes, err := json.Marshal(&id)
	if err != nil {
		return
	}

	err = ioutil.WriteFile(idpath, bytes, 0600)
	return
}

func loadIdentity(idpath string) (id Identity, err error) {
	bytes, err := ioutil.ReadFile(idpath)
	if err != nil {
		return
	}

	err = json.Unmarshal(bytes, &id)
	return
}

func getPrivateKey(priv PrivateId) (p2p_crypto.PrivKey, error) {
	bytes, err := decryptPrivateId(priv)
	if err != nil {
		return nil, err
	}

	return p2p_crypto.UnmarshalPrivateKey(bytes)
}

// private key encryption/decryption:
//  key derivation with scrypt
//  encryption with nacl secretbox
func encryptPrivateId(priv *PrivateId, data []byte) error {
	var (
		salt  [16]byte
		nonce [24]byte
		key   [32]byte
	)

	_, err := rand.Read(salt[:])
	if err != nil {
		return err
	}

	_, err = rand.Read(nonce[:])
	if err != nil {
		return err
	}

	pass, err := getEncryptionPass()
	if err != nil {
		return err
	}

	xkey, err := scrypt.Key(pass, salt[:], ScryptN, ScryptR, ScryptP, 32)
	if err != nil {
		return err
	}

	copy(key[:], xkey)

	ctext := sbox.Seal(nil, data, &nonce, &key)

	priv.Salt = salt[:]
	priv.Nonce = nonce[:]
	priv.Data = ctext[:]
	priv.Params.N = ScryptN
	priv.Params.R = ScryptR
	priv.Params.P = ScryptP

	return nil
}

func decryptPrivateId(priv PrivateId) ([]byte, error) {
	var (
		nonce [24]byte
		key   [32]byte
	)

	pass, err := getDecryptionPass()
	if err != nil {
		return nil, err
	}

	xkey, err := scrypt.Key(pass, priv.Salt, priv.Params.N, priv.Params.R, priv.Params.P, 32)
	if err != nil {
		return nil, err
	}

	copy(nonce[:], priv.Nonce)
	copy(key[:], xkey)

	bytes, ok := sbox.Open(nil, priv.Data, &nonce, &key)
	if !ok {
		return nil, DecryptionError
	}

	return bytes, nil
}

func getEncryptionPass() ([]byte, error) {
	for {
		fmt.Fprintf(os.Stderr, "Enter passphrase: ")
		pass1, err := gopass.GetPasswd()
		if err != nil {
			return nil, err
		}

		fmt.Fprintf(os.Stderr, "Re-enter passphrase: ")
		pass2, err := gopass.GetPasswd()
		if err != nil {
			return nil, err
		}

		if bytes.Equal(pass1, pass2) {
			return pass1, nil
		}

		fmt.Fprintln(os.Stderr, "Passphrases don't match")
	}
}

func getDecryptionPass() ([]byte, error) {
	fmt.Fprintf(os.Stderr, "Enter passphrase: ")
	return gopass.GetPasswd()
}
<|endoftext|>


<commit_before>package environment

import (
	"fmt"
	"github.com/docker/go-connections/nat"
	"github.com/pterodactyl/wings/config"
	"strconv"
)

// Defines the allocations available for a given server. When using the Docker environment
// driver these correspond to mappings for the container that allow external connections.
type Allocations struct {
	// Defines the default allocation that should be used for this server. This is
	// what will be used for {SERVER_IP} and {SERVER_PORT} when modifying configuration
	// files or the startup arguments for a server.
	DefaultMapping struct {
		Ip   string `json:"ip"`
		Port int    `json:"port"`
	} `json:"default"`

	// Mappings contains all of the ports that should be assigned to a given server
	// attached to the IP they correspond to.
	Mappings map[string][]int `json:"mappings"`
}

// Converts the server allocation mappings into a format that can be understood by Docker. While
// we do strive to support multiple environments, using Docker's standardized format for the
// bindings certainly makes life a little easier for managing things.
//
// You'll want to use DockerBindings() if you need to re-map 127.0.0.1 to the Docker interface.
func (a *Allocations) Bindings() nat.PortMap {
	var out = nat.PortMap{}

	for ip, ports := range a.Mappings {
		for _, port := range ports {
			// Skip over invalid ports.
			if port < 1 || port > 65535 {
				continue
			}

			binding := nat.PortBinding{
				HostIP:   ip,
				HostPort: strconv.Itoa(port),
			}

			if tcpPort, ok := out[nat.Port(fmt.Sprintf("%d/tcp", port))]; !ok {
				out[nat.Port(fmt.Sprintf("%d/tcp", port))] = []nat.PortBinding{binding}
			} else {
				out[nat.Port(fmt.Sprintf("%d/tcp", port))] = append(tcpPort, binding)
			}

			if udpPort, ok := out[nat.Port(fmt.Sprintf("%d/udp", port))]; !ok {
				out[nat.Port(fmt.Sprintf("%d/udp", port))] = []nat.PortBinding{binding}
			} else {
				out[nat.Port(fmt.Sprintf("%d/udp", port))] = append(udpPort, binding)
			}
		}
	}

	return out
}

// Returns the bindings for the server in a way that is supported correctly by Docker. This replaces
// any reference to 127.0.0.1 with the IP of the pterodactyl0 network interface which will allow the
// server to operate on a local address while still being accessible by other containers.
func (a *Allocations) DockerBindings() nat.PortMap {
	iface := config.Get().Docker.Network.Interface

	out := a.Bindings()
	// Loop over all of the bindings for this container, and convert any that reference 127.0.0.1
	// to use the pterodactyl0 network interface IP, as that is the true local for what people are
	// trying to do when creating servers.
	for p, binds := range out {
		for i, alloc := range binds {
			if alloc.HostIP != "127.0.0.1" {
				continue
			}

			// If using ISPN just delete the local allocation from the server.
			if config.Get().Docker.Network.ISPN {
				out[p] = append(out[p][:i], out[p][i+1:]...)
			} else {
				out[p][i] = nat.PortBinding{
					HostIP:   iface,
					HostPort: alloc.HostPort,
				}
			}
		}
	}

	return out
}

// Converts the server allocation mappings into a PortSet that can be understood
// by Docker. This formatting is slightly different than "Bindings" as it should
// return an empty struct rather than a binding.
//
// To accomplish this, we'll just get the values from "DockerBindings" and then set them
// to empty structs. Because why not.
func (a *Allocations) Exposed() nat.PortSet {
	var out = nat.PortSet{}

	for port := range a.DockerBindings() {
		out[port] = struct{}{}
	}

	return out
}
<commit_msg>Update allocations.go<commit_after>package environment

import (
	"fmt"
	"github.com/docker/go-connections/nat"
	"github.com/pterodactyl/wings/config"
	"strconv"
)

// Defines the allocations available for a given server. When using the Docker environment
// driver these correspond to mappings for the container that allow external connections.
type Allocations struct {
	// Defines the default allocation that should be used for this server. This is
	// what will be used for {SERVER_IP} and {SERVER_PORT} when modifying configuration
	// files or the startup arguments for a server.
	DefaultMapping struct {
		Ip   string `json:"ip"`
		Port int    `json:"port"`
	} `json:"default"`

	// Mappings contains all of the ports that should be assigned to a given server
	// attached to the IP they correspond to.
	Mappings map[string][]int `json:"mappings"`
}

// Converts the server allocation mappings into a format that can be understood by Docker. While
// we do strive to support multiple environments, using Docker's standardized format for the
// bindings certainly makes life a little easier for managing things.
//
// You'll want to use DockerBindings() if you need to re-map 127.0.0.1 to the Docker interface.
func (a *Allocations) Bindings() nat.PortMap {
	var out = nat.PortMap{}

	for ip, ports := range a.Mappings {
		for _, port := range ports {
			// Skip over invalid ports.
			if port < 1 || port > 65535 {
				continue
			}

			binding := nat.PortBinding{
				HostIP:   ip,
				HostPort: strconv.Itoa(port),
			}

			if v, ok := out[nat.Port(fmt.Sprintf("%d/tcp", port))]; ok {
				out[nat.Port(fmt.Sprintf("%d/tcp", port))] = append(v, binding)
			} else {
				out[nat.Port(fmt.Sprintf("%d/tcp", port))] = []nat.PortBinding{binding}
			}

			if v, ok := out[nat.Port(fmt.Sprintf("%d/udp", port))]; ok {
				out[nat.Port(fmt.Sprintf("%d/udp", port))] = append(v, binding)
			} else {
				out[nat.Port(fmt.Sprintf("%d/udp", port))] = []nat.PortBinding{binding}
			}
		}
	}

	return out
}

// Returns the bindings for the server in a way that is supported correctly by Docker. This replaces
// any reference to 127.0.0.1 with the IP of the pterodactyl0 network interface which will allow the
// server to operate on a local address while still being accessible by other containers.
func (a *Allocations) DockerBindings() nat.PortMap {
	iface := config.Get().Docker.Network.Interface

	out := a.Bindings()
	// Loop over all of the bindings for this container, and convert any that reference 127.0.0.1
	// to use the pterodactyl0 network interface IP, as that is the true local for what people are
	// trying to do when creating servers.
	for p, binds := range out {
		for i, alloc := range binds {
			if alloc.HostIP != "127.0.0.1" {
				continue
			}

			// If using ISPN just delete the local allocation from the server.
			if config.Get().Docker.Network.ISPN {
				out[p] = append(out[p][:i], out[p][i+1:]...)
			} else {
				out[p][i] = nat.PortBinding{
					HostIP:   iface,
					HostPort: alloc.HostPort,
				}
			}
		}
	}

	return out
}

// Converts the server allocation mappings into a PortSet that can be understood
// by Docker. This formatting is slightly different than "Bindings" as it should
// return an empty struct rather than a binding.
//
// To accomplish this, we'll just get the values from "DockerBindings" and then set them
// to empty structs. Because why not.
func (a *Allocations) Exposed() nat.PortSet {
	var out = nat.PortSet{}

	for port := range a.DockerBindings() {
		out[port] = struct{}{}
	}

	return out
}
<|endoftext|>


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

package sync_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	gc "launchpad.net/gocheck"

	agenttools "launchpad.net/juju-core/agent/tools"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/sync"
	envtesting "launchpad.net/juju-core/environs/testing"
	"launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/provider/dummy"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
)

func TestPackage(t *testing.T) {
	gc.TestingT(t)
}

type syncSuite struct {
	coretesting.LoggingSuite
	home         *coretesting.FakeHome
	targetEnv    environs.Environ
	origVersion  version.Binary
	origLocation string
	storage      *envtesting.EC2HTTPTestStorage
	localStorage string
}

var _ = gc.Suite(&syncSuite{})

func (s *syncSuite) setUpTest(c *gc.C) {
	s.LoggingSuite.SetUpTest(c)
	s.origVersion = version.Current
	// It's important that this be v1 to match the test data.
	version.Current.Number = version.MustParse("1.2.3")

	// Create a target environments.yaml and make sure its environment is emptyPublic.
	s.home = coretesting.MakeFakeHome(c, `
environments:
    test-target:
        type: dummy
        state-server: false
        authorized-keys: "not-really-one"
`)
	var err error
	s.targetEnv, err = environs.PrepareFromName("test-target")
	c.Assert(err, gc.IsNil)
	envtesting.RemoveAllTools(c, s.targetEnv)

	// Create a source storage.
	s.storage, err = envtesting.NewEC2HTTPTestStorage("127.0.0.1")
	c.Assert(err, gc.IsNil)

	// Create a local tools directory.
	s.localStorage = c.MkDir()

	// Populate both with the public tools.
	for _, vers := range vAll {
		s.storage.PutBinary(vers)
		putBinary(c, s.localStorage, vers)
	}

	// Switch tools location.
	s.origLocation = sync.DefaultToolsLocation
	sync.DefaultToolsLocation = s.storage.Location()
}

func (s *syncSuite) tearDownTest(c *gc.C) {
	c.Assert(s.storage.Stop(), gc.IsNil)
	sync.DefaultToolsLocation = s.origLocation
	dummy.Reset()
	s.home.Restore()
	version.Current = s.origVersion
	s.LoggingSuite.TearDownTest(c)
}

var tests = []struct {
	description string
	ctx         *sync.SyncContext
	source      bool
	tools       []version.Binary
	emptyPublic bool
}{
	{
		description: "copy newest from the filesystem",
		ctx:         &sync.SyncContext{},
		source:      true,
		tools:       v100all,
		emptyPublic: true,
	},
	{
		description: "copy newest from the dummy environment",
		ctx:         &sync.SyncContext{},
		tools:       v100all,
		emptyPublic: true,
	},
	{
		description: "copy newest dev from the dummy environment",
		ctx: &sync.SyncContext{
			Dev: true,
		},
		tools:       v190all,
		emptyPublic: true,
	},
	{
		description: "copy all from the dummy environment",
		ctx: &sync.SyncContext{
			AllVersions: true,
		},
		tools:       v100all,
		emptyPublic: true,
	},
	{
		description: "copy all and dev from the dummy environment",
		ctx: &sync.SyncContext{
			AllVersions: true,
			Dev:         true,
		},
		tools:       v1all,
		emptyPublic: true,
	},
	{
		description: "copy to the dummy environment public storage",
		ctx: &sync.SyncContext{
			PublicBucket: true,
		},
		tools:       v100all,
		emptyPublic: false,
	},
}

func (s *syncSuite) TestSyncing(c *gc.C) {
	for i, test := range tests {
		// Perform all tests in a "clean" environment.
		func() {
			s.setUpTest(c)
			defer s.tearDownTest(c)

			c.Logf("test %d: %s", i, test.description)

			if test.source {
				test.ctx.Source = s.localStorage
			}
			test.ctx.Target = s.targetEnv

			c.Logf("x1")
			err := sync.SyncTools(test.ctx)
			c.Assert(err, gc.IsNil)
			c.Logf("x2")

			targetTools, err := tools.FindAvailableTools(s.targetEnv, 1)
			c.Assert(err, gc.IsNil)
			assertToolsList(c, targetTools, test.tools)
			c.Logf("x3")

			if test.emptyPublic {
				c.Logf("x4")
				assertEmpty(c, s.targetEnv.PublicStorage())
			} else {
				c.Logf("x5")
				assertEmpty(c, s.targetEnv.Storage())
			}
		}()
	}
}

func (s *syncSuite) TestCopyToDummyPublicBlockedByPrivate(c *gc.C) {
	s.setUpTest(c)
	defer s.tearDownTest(c)

	envtesting.UploadFakeToolsVersion(c, s.targetEnv.Storage(), v200p64)
	ctx := &sync.SyncContext{
		Target:       s.targetEnv,
		PublicBucket: true,
	}
	err := sync.SyncTools(ctx)
	c.Assert(err, gc.ErrorMatches, "private tools present: public tools would be ignored")
	assertEmpty(c, s.targetEnv.PublicStorage())
}

var (
	v100p64 = version.MustParseBinary("1.0.0-precise-amd64")
	v100q64 = version.MustParseBinary("1.0.0-quantal-amd64")
	v100q32 = version.MustParseBinary("1.0.0-quantal-i386")
	v100all = []version.Binary{v100p64, v100q64, v100q32}
	v190q64 = version.MustParseBinary("1.9.0-quantal-amd64")
	v190p32 = version.MustParseBinary("1.9.0-precise-i386")
	v190all = []version.Binary{v190q64, v190p32}
	v1all   = append(v100all, v190all...)
	v200p64 = version.MustParseBinary("2.0.0-precise-amd64")
	vAll    = append(v1all, v200p64)
)

// putBinary stores a faked binary in the test directory.
func putBinary(c *gc.C, storagePath string, v version.Binary) {
	data := v.String()
	name := agenttools.StorageName(v)
	filename := filepath.Join(storagePath, name)
	dir := filepath.Dir(filename)
	err := os.MkdirAll(dir, 0755)
	c.Assert(err, gc.IsNil)
	err = ioutil.WriteFile(filename, []byte(data), 0666)
	c.Assert(err, gc.IsNil)
}

func assertEmpty(c *gc.C, storage environs.StorageReader) {
	list, err := agenttools.ReadList(storage, 1)
	if len(list) > 0 {
		c.Logf("got unexpected tools: %s", list)
	}
	c.Assert(err, gc.Equals, agenttools.ErrNoTools)
}

func assertToolsList(c *gc.C, list agenttools.List, expected []version.Binary) {
	urls := list.URLs()
	c.Check(urls, gc.HasLen, len(expected))
	for _, vers := range expected {
		c.Assert(urls[vers], gc.Not(gc.Equals), "")
	}
}
<commit_msg>remove debug relics<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package sync_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	gc "launchpad.net/gocheck"

	agenttools "launchpad.net/juju-core/agent/tools"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/sync"
	envtesting "launchpad.net/juju-core/environs/testing"
	"launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/provider/dummy"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
)

func TestPackage(t *testing.T) {
	gc.TestingT(t)
}

type syncSuite struct {
	coretesting.LoggingSuite
	home         *coretesting.FakeHome
	targetEnv    environs.Environ
	origVersion  version.Binary
	origLocation string
	storage      *envtesting.EC2HTTPTestStorage
	localStorage string
}

var _ = gc.Suite(&syncSuite{})

func (s *syncSuite) setUpTest(c *gc.C) {
	s.LoggingSuite.SetUpTest(c)
	s.origVersion = version.Current
	// It's important that this be v1 to match the test data.
	version.Current.Number = version.MustParse("1.2.3")

	// Create a target environments.yaml and make sure its environment is emptyPublic.
	s.home = coretesting.MakeFakeHome(c, `
environments:
    test-target:
        type: dummy
        state-server: false
        authorized-keys: "not-really-one"
`)
	var err error
	s.targetEnv, err = environs.PrepareFromName("test-target")
	c.Assert(err, gc.IsNil)
	envtesting.RemoveAllTools(c, s.targetEnv)

	// Create a source storage.
	s.storage, err = envtesting.NewEC2HTTPTestStorage("127.0.0.1")
	c.Assert(err, gc.IsNil)

	// Create a local tools directory.
	s.localStorage = c.MkDir()

	// Populate both with the public tools.
	for _, vers := range vAll {
		s.storage.PutBinary(vers)
		putBinary(c, s.localStorage, vers)
	}

	// Switch tools location.
	s.origLocation = sync.DefaultToolsLocation
	sync.DefaultToolsLocation = s.storage.Location()
}

func (s *syncSuite) tearDownTest(c *gc.C) {
	c.Assert(s.storage.Stop(), gc.IsNil)
	sync.DefaultToolsLocation = s.origLocation
	dummy.Reset()
	s.home.Restore()
	version.Current = s.origVersion
	s.LoggingSuite.TearDownTest(c)
}

var tests = []struct {
	description string
	ctx         *sync.SyncContext
	source      bool
	tools       []version.Binary
	emptyPublic bool
}{
	{
		description: "copy newest from the filesystem",
		ctx:         &sync.SyncContext{},
		source:      true,
		tools:       v100all,
		emptyPublic: true,
	},
	{
		description: "copy newest from the dummy environment",
		ctx:         &sync.SyncContext{},
		tools:       v100all,
		emptyPublic: true,
	},
	{
		description: "copy newest dev from the dummy environment",
		ctx: &sync.SyncContext{
			Dev: true,
		},
		tools:       v190all,
		emptyPublic: true,
	},
	{
		description: "copy all from the dummy environment",
		ctx: &sync.SyncContext{
			AllVersions: true,
		},
		tools:       v100all,
		emptyPublic: true,
	},
	{
		description: "copy all and dev from the dummy environment",
		ctx: &sync.SyncContext{
			AllVersions: true,
			Dev:         true,
		},
		tools:       v1all,
		emptyPublic: true,
	},
	{
		description: "copy to the dummy environment public storage",
		ctx: &sync.SyncContext{
			PublicBucket: true,
		},
		tools:       v100all,
		emptyPublic: false,
	},
}

func (s *syncSuite) TestSyncing(c *gc.C) {
	for i, test := range tests {
		// Perform all tests in a "clean" environment.
		func() {
			s.setUpTest(c)
			defer s.tearDownTest(c)

			c.Logf("test %d: %s", i, test.description)

			if test.source {
				test.ctx.Source = s.localStorage
			}
			test.ctx.Target = s.targetEnv

			err := sync.SyncTools(test.ctx)
			c.Assert(err, gc.IsNil)

			targetTools, err := tools.FindAvailableTools(s.targetEnv, 1)
			c.Assert(err, gc.IsNil)
			assertToolsList(c, targetTools, test.tools)

			if test.emptyPublic {
				assertEmpty(c, s.targetEnv.PublicStorage())
			} else {
				assertEmpty(c, s.targetEnv.Storage())
			}
		}()
	}
}

func (s *syncSuite) TestCopyToDummyPublicBlockedByPrivate(c *gc.C) {
	s.setUpTest(c)
	defer s.tearDownTest(c)

	envtesting.UploadFakeToolsVersion(c, s.targetEnv.Storage(), v200p64)
	ctx := &sync.SyncContext{
		Target:       s.targetEnv,
		PublicBucket: true,
	}
	err := sync.SyncTools(ctx)
	c.Assert(err, gc.ErrorMatches, "private tools present: public tools would be ignored")
	assertEmpty(c, s.targetEnv.PublicStorage())
}

var (
	v100p64 = version.MustParseBinary("1.0.0-precise-amd64")
	v100q64 = version.MustParseBinary("1.0.0-quantal-amd64")
	v100q32 = version.MustParseBinary("1.0.0-quantal-i386")
	v100all = []version.Binary{v100p64, v100q64, v100q32}
	v190q64 = version.MustParseBinary("1.9.0-quantal-amd64")
	v190p32 = version.MustParseBinary("1.9.0-precise-i386")
	v190all = []version.Binary{v190q64, v190p32}
	v1all   = append(v100all, v190all...)
	v200p64 = version.MustParseBinary("2.0.0-precise-amd64")
	vAll    = append(v1all, v200p64)
)

// putBinary stores a faked binary in the test directory.
func putBinary(c *gc.C, storagePath string, v version.Binary) {
	data := v.String()
	name := agenttools.StorageName(v)
	filename := filepath.Join(storagePath, name)
	dir := filepath.Dir(filename)
	err := os.MkdirAll(dir, 0755)
	c.Assert(err, gc.IsNil)
	err = ioutil.WriteFile(filename, []byte(data), 0666)
	c.Assert(err, gc.IsNil)
}

func assertEmpty(c *gc.C, storage environs.StorageReader) {
	list, err := agenttools.ReadList(storage, 1)
	if len(list) > 0 {
		c.Logf("got unexpected tools: %s", list)
	}
	c.Assert(err, gc.Equals, agenttools.ErrNoTools)
}

func assertToolsList(c *gc.C, list agenttools.List, expected []version.Binary) {
	urls := list.URLs()
	c.Check(urls, gc.HasLen, len(expected))
	for _, vers := range expected {
		c.Assert(urls[vers], gc.Not(gc.Equals), "")
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-go 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 pathutil

import (
	"go/build"
	"path/filepath"

	"github.com/pkg/errors"
)

// parsePackage search the parent directory of dir with the recursive loop.
func parsePackage(dir string) (*build.Package, error) {
	dir = filepath.Clean(dir)

	// for save the before(child) package information
	savePkg := new(build.Package)
	for {
		// Raise the error if dir is reaches root("/") or GOPATH or GOROOT
		if dir == "/" || dir == build.Default.GOPATH || dir == build.Default.GOROOT {
			return nil, errors.New("couldn't find the package")
		}

		// Get the current dir package information
		pkg, err := build.Default.ImportDir(dir, build.ImportMode(0))
		if err != nil {
			// Check the exists .go file in the dir
			if _, ok := err.(*build.NoGoError); ok {
				// Case of recursive loop of second and subsequent,
				// Use child(before) directory if NoGoError
				if savePkg.Dir != "" {
					return savePkg, nil
				} else {
					savePkg = pkg
					dir = filepath.Dir(dir)
					continue
				}
			} else if err != nil { // Failed ImportDir() on other errors
				return nil, errors.WithStack(err)
			}
		}

		// return the current directory if package is main
		if pkg.IsCommand() {
			return pkg, nil
		}

		// Return the child(before) package directory if different to current package name
		if beforePkgName := savePkg.Name; beforePkgName != "" && beforePkgName != pkg.Name {
			return savePkg, nil
		}

		// Save the current package and re-assign dir to parent dir for the next recursive loop
		savePkg = pkg
		dir = filepath.Dir(dir)
	}
}

// PackagePath returns the *full path* of package directory estimated
// from the dir directory structure.
// like:
//  return "/Users/zchee/go/src/github.com/pkg/errors", nil
func PackagePath(dir string) (string, error) {
	pkg, err := parsePackage(dir)
	if err != nil {
		return "", err
	}

	return pkg.Dir, nil
}

// PackageID returns the package ID(ImportPath) estimated from the dir
// directory structure.
// like:
//  return "github.com/pkg/errors", nil
func PackageID(dir string) (string, error) {
	pkg, err := parsePackage(dir)
	if err != nil {
		return "", err
	}

	return pkg.ImportPath, nil
}
<commit_msg>nvim-go/pathutil: add PackageRoot function<commit_after>// Copyright 2016 The nvim-go 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 pathutil

import (
	"go/build"
	"path/filepath"

	"github.com/pkg/errors"
)

// parsePackage search the parent directory of dir with the recursive loop.
func parsePackage(dir string) (*build.Package, error) {
	dir = filepath.Clean(dir)

	// for save the before(child) package information
	savePkg := new(build.Package)
	for {
		// Raise the error if dir is reaches root("/") or GOPATH or GOROOT
		if dir == "/" || dir == build.Default.GOPATH || dir == build.Default.GOROOT {
			return nil, errors.New("couldn't find the package")
		}

		// Get the current dir package information
		pkg, err := build.Default.ImportDir(dir, build.ImportMode(0))
		if err != nil {
			// Check the exists .go file in the dir
			if _, ok := err.(*build.NoGoError); ok {
				// Case of recursive loop of second and subsequent,
				// Use child(before) directory if NoGoError
				if savePkg.Dir != "" {
					return savePkg, nil
				} else {
					savePkg = pkg
					dir = filepath.Dir(dir)
					continue
				}
			} else if err != nil { // Failed ImportDir() on other errors
				return nil, errors.WithStack(err)
			}
		}

		// return the current directory if package is main
		if pkg.IsCommand() {
			return pkg, nil
		}

		// Return the child(before) package directory if different to current package name
		if beforePkgName := savePkg.Name; beforePkgName != "" && beforePkgName != pkg.Name {
			return savePkg, nil
		}

		// Save the current package and re-assign dir to parent dir for the next recursive loop
		savePkg = pkg
		dir = filepath.Dir(dir)
	}
}

// PackagePath returns the *full path* of package directory estimated
// from the dir directory structure.
// like:
//  return "/Users/zchee/go/src/github.com/pkg/errors", nil
func PackagePath(dir string) (string, error) {
	pkg, err := parsePackage(dir)
	if err != nil {
		return "", err
	}

	return pkg.Dir, nil
}

// PackageID returns the package ID(ImportPath) estimated from the dir
// directory structure.
// like:
//  return "github.com/pkg/errors", nil
func PackageID(dir string) (string, error) {
	pkg, err := parsePackage(dir)
	if err != nil {
		return "", err
	}

	return pkg.ImportPath, nil
}

// PackageRoot finds repository root of package from path.
// Return the '/root' if path is '/root/foo/bar'.
// If path package directory archtecture uses '/root/src/foo', returns the '/root/src'.
func PackageRoot(path string) (*build.Package, error) {
	if !IsDir(path) {
		path = filepath.Dir(path)
	}
	path = filepath.Clean(path)

	// for save the before(child) package information
	savePkg := new(build.Package)
	for {
		// Raise the error if dir is reaches root("/") or GOPATH or GOROOT
		if path == "/" || path == build.Default.GOPATH || path == build.Default.GOROOT {
			return nil, errors.New("couldn't find the package")
		}

		// Get the current dir package information
		pkg, err := build.Default.ImportDir(path, build.ImportMode(0))
		if err != nil {
			// Check the exists .go file in the dir
			if _, ok := err.(*build.NoGoError); ok {
				// Case of recursive loop of second and subsequent,
				// Use child(before) directory if NoGoError
				if savePkg.Dir != "" {
					return savePkg, nil
				} else {
					savePkg = pkg
					path = filepath.Dir(path)
					continue
				}
			} else if err != nil { // Failed ImportDir() on other errors
				return nil, errors.WithStack(err)
			}
		}

		// return the current directory if package is main
		if pkg.IsCommand() {
			return pkg, nil
		}

		// Return the child(before) package directory if different to current package name
		if beforePkgName := savePkg.Name; beforePkgName != "" && beforePkgName != pkg.Name {
			return savePkg, nil
		}

		// Save the current package and re-assign dir to parent dir for the next recursive loop
		savePkg = pkg
		path = filepath.Dir(path)
	}
}
<|endoftext|>


<commit_before>package monitor

import (
	"github.com/abustany/gollery/utils"
	"github.com/howeyc/fsnotify"
	"github.com/robfig/revel"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
)

type Event interface {
	Path() string
}

type CreateEvent struct {
	path string
	Info os.FileInfo
}

type DeleteEvent struct {
	path        string
	IsDirectory bool
}

type Monitor struct {
	monitor          *fsnotify.Watcher
	watchedDirs      map[string]uint64 // maps path to inode to detect renames
	watchedDirsMutex sync.Mutex
	listeners        map[chan<- Event]bool
}

func (e *CreateEvent) Path() string {
	return e.path
}

func (e *DeleteEvent) Path() string {
	return e.path
}

func (m *Monitor) IsDirectoryWatched(filePath string) bool {
	m.watchedDirsMutex.Lock()
	defer m.watchedDirsMutex.Unlock()

	_, watched := m.watchedDirs[filePath]

	return watched
}

func (m *Monitor) dispatchEvent(ev Event) {
	for c, _ := range m.listeners {
		select {
		case c <- ev:
			continue
		default:
			continue
		}
	}
}

func (m *Monitor) monitorRoutine() {
	for {
		select {
		case ev := <-m.monitor.Event:
			revel.TRACE.Printf("Thumbnailer: file event: %s", ev)

			basename := path.Base(ev.Name)

			if len(basename) > 0 && basename[0] == '.' {
				revel.TRACE.Printf("Skipping event for hidden file %s", ev.Name)
				continue
			}

			if ev.IsDelete() || ev.IsRename() {
				isDir := m.IsDirectoryWatched(ev.Name)

				if isDir {
					m.watchedDirsMutex.Lock()
					delete(m.watchedDirs, ev.Name)
					m.watchedDirsMutex.Unlock()
				}

				m.dispatchEvent(&DeleteEvent{
					ev.Name,
					isDir,
				})
				continue
			}

			info, err := os.Stat(ev.Name)

			if err != nil {
				revel.ERROR.Printf("Cannot get file information for %s: %s", ev.Name, err)
				continue
			}

			if info.IsDir() {
				err = m.setupDirMonitor(ev.Name, info, nil)

				if err != nil {
					revel.ERROR.Printf("Cannot setup monitoring for new directory %s: %s", ev.Name, err)
				}
			}

			m.dispatchEvent(&CreateEvent{
				ev.Name,
				info,
			})
		case err := <-m.monitor.Error:
			revel.ERROR.Printf("Thumbnailer: file monitoring error: %s", err)
		}
	}
}

func NewMonitor() (*Monitor, error) {
	m := &Monitor{
		watchedDirs: map[string]uint64{},
		listeners:   map[chan<- Event]bool{},
	}

	var err error

	m.monitor, err = fsnotify.NewWatcher()

	if err != nil {
		return nil, err
	}

	go m.monitorRoutine()

	return m, nil
}

func (m *Monitor) setupDirMonitor(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if strings.HasPrefix(path, ".") {
		return filepath.SkipDir
	}

	if !info.IsDir() {
		return nil
	}

	err = m.monitor.Watch(path)

	inode := uint64(0)

	if x, ok := info.Sys().(*syscall.Stat_t); ok {
		inode = x.Ino
	}

	m.watchedDirsMutex.Lock()
	m.watchedDirs[path] = inode
	m.watchedDirsMutex.Unlock()

	if err == nil {
		revel.INFO.Printf("Setup a directory monitor on '%s'", path)
	}

	return err
}

func (m *Monitor) Watch(dir string) error {
	return utils.WrapError(filepath.Walk(dir, m.setupDirMonitor), "Cannot setup monitoring")
}

func (m *Monitor) Listen(eventChannel chan<- Event) {
	m.listeners[eventChannel] = true
}

func (m *Monitor) Unlisten(eventChannel chan<- Event) {
	delete(m.listeners, eventChannel)
}
<commit_msg>monitor: Add a method to list watched directories<commit_after>package monitor

import (
	"github.com/abustany/gollery/utils"
	"github.com/howeyc/fsnotify"
	"github.com/robfig/revel"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
)

type Event interface {
	Path() string
}

type CreateEvent struct {
	path string
	Info os.FileInfo
}

type DeleteEvent struct {
	path        string
	IsDirectory bool
}

type Monitor struct {
	monitor          *fsnotify.Watcher
	watchedDirs      map[string]uint64 // maps path to inode to detect renames
	watchedDirsMutex sync.Mutex
	listeners        map[chan<- Event]bool
}

func (e *CreateEvent) Path() string {
	return e.path
}

func (e *DeleteEvent) Path() string {
	return e.path
}

func (m *Monitor) IsDirectoryWatched(filePath string) bool {
	m.watchedDirsMutex.Lock()
	defer m.watchedDirsMutex.Unlock()

	_, watched := m.watchedDirs[filePath]

	return watched
}

func (m *Monitor) dispatchEvent(ev Event) {
	for c, _ := range m.listeners {
		select {
		case c <- ev:
			continue
		default:
			continue
		}
	}
}

func (m *Monitor) monitorRoutine() {
	for {
		select {
		case ev := <-m.monitor.Event:
			revel.TRACE.Printf("Thumbnailer: file event: %s", ev)

			basename := path.Base(ev.Name)

			if len(basename) > 0 && basename[0] == '.' {
				revel.TRACE.Printf("Skipping event for hidden file %s", ev.Name)
				continue
			}

			if ev.IsDelete() || ev.IsRename() {
				isDir := m.IsDirectoryWatched(ev.Name)

				if isDir {
					m.watchedDirsMutex.Lock()
					delete(m.watchedDirs, ev.Name)
					m.watchedDirsMutex.Unlock()
				}

				m.dispatchEvent(&DeleteEvent{
					ev.Name,
					isDir,
				})
				continue
			}

			info, err := os.Stat(ev.Name)

			if err != nil {
				revel.ERROR.Printf("Cannot get file information for %s: %s", ev.Name, err)
				continue
			}

			if info.IsDir() {
				err = m.setupDirMonitor(ev.Name, info, nil)

				if err != nil {
					revel.ERROR.Printf("Cannot setup monitoring for new directory %s: %s", ev.Name, err)
				}
			}

			m.dispatchEvent(&CreateEvent{
				ev.Name,
				info,
			})
		case err := <-m.monitor.Error:
			revel.ERROR.Printf("Thumbnailer: file monitoring error: %s", err)
		}
	}
}

func NewMonitor() (*Monitor, error) {
	m := &Monitor{
		watchedDirs: map[string]uint64{},
		listeners:   map[chan<- Event]bool{},
	}

	var err error

	m.monitor, err = fsnotify.NewWatcher()

	if err != nil {
		return nil, err
	}

	go m.monitorRoutine()

	return m, nil
}

func (m *Monitor) setupDirMonitor(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if strings.HasPrefix(path, ".") {
		return filepath.SkipDir
	}

	if !info.IsDir() {
		return nil
	}

	err = m.monitor.Watch(path)

	inode := uint64(0)

	if x, ok := info.Sys().(*syscall.Stat_t); ok {
		inode = x.Ino
	}

	m.watchedDirsMutex.Lock()
	m.watchedDirs[path] = inode
	m.watchedDirsMutex.Unlock()

	if err == nil {
		revel.INFO.Printf("Setup a directory monitor on '%s'", path)
	}

	return err
}

func (m *Monitor) Watch(dir string) error {
	return utils.WrapError(filepath.Walk(dir, m.setupDirMonitor), "Cannot setup monitoring")
}

func (m *Monitor) Listen(eventChannel chan<- Event) {
	m.listeners[eventChannel] = true
}

func (m *Monitor) Unlisten(eventChannel chan<- Event) {
	delete(m.listeners, eventChannel)
}

func (m *Monitor) WatchedDirectories() []string {
	m.watchedDirsMutex.Lock()
	defer m.watchedDirsMutex.Unlock()

	dirs := make([]string, len(m.watchedDirs))
	i := 0

	for dir, _ := range m.watchedDirs {
		dirs[i] = dir
		i++
	}

	return dirs
}
<|endoftext|>


<commit_before>package client

import (
	"strings"
	"time"

	"github.com/micro/micro/v3/internal/auth/rules"
	"github.com/micro/micro/v3/internal/auth/token"
	"github.com/micro/micro/v3/internal/auth/token/jwt"
	pb "github.com/micro/micro/v3/proto/auth"
	"github.com/micro/micro/v3/service/auth"
	"github.com/micro/micro/v3/service/client"
	"github.com/micro/micro/v3/service/client/cache"
	"github.com/micro/micro/v3/service/context"
	"github.com/micro/micro/v3/service/errors"
)

// srv is the service implementation of the Auth interface
type srv struct {
	options auth.Options
	auth    pb.AuthService
	rules   pb.RulesService
	token   token.Provider
}

func (s *srv) String() string {
	return "service"
}

func (s *srv) Init(opts ...auth.Option) {
	for _, o := range opts {
		o(&s.options)
	}
	s.auth = pb.NewAuthService("auth", client.DefaultClient)
	s.rules = pb.NewRulesService("auth", client.DefaultClient)
	s.setupJWT()
}

func (s *srv) Options() auth.Options {
	return s.options
}

// Generate a new account
func (s *srv) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
	options := auth.NewGenerateOptions(opts...)
	if len(options.Issuer) == 0 {
		options.Issuer = s.options.Issuer
	}

	// we have the JWT private key and generate ourselves an account
	if len(s.options.PrivateKey) > 0 {
		acc := &auth.Account{
			ID:       id,
			Type:     options.Type,
			Scopes:   options.Scopes,
			Metadata: options.Metadata,
			Issuer:   options.Issuer,
		}

		tok, err := s.token.Generate(acc, token.WithExpiry(time.Hour*24*365))
		if err != nil {
			return nil, err
		}

		// when using JWTs, the account secret is the JWT's token. This
		// can be used as an argument in the Token method.
		acc.Secret = tok.Token
		return acc, nil
	}

	rsp, err := s.auth.Generate(context.DefaultContext, &pb.GenerateRequest{
		Id:       id,
		Type:     options.Type,
		Secret:   options.Secret,
		Scopes:   options.Scopes,
		Metadata: options.Metadata,
		Provider: options.Provider,
		Options: &pb.Options{
			Namespace: options.Issuer,
		},
		Name: options.Name,
	}, s.callOpts()...)
	if err != nil {
		return nil, err
	}

	return serializeAccount(rsp.Account), nil
}

// Grant access to a resource
func (s *srv) Grant(rule *auth.Rule) error {
	access := pb.Access_UNKNOWN
	if rule.Access == auth.AccessGranted {
		access = pb.Access_GRANTED
	} else if rule.Access == auth.AccessDenied {
		access = pb.Access_DENIED
	}

	_, err := s.rules.Create(context.DefaultContext, &pb.CreateRequest{
		Rule: &pb.Rule{
			Id:       rule.ID,
			Scope:    rule.Scope,
			Priority: rule.Priority,
			Access:   access,
			Resource: &pb.Resource{
				Type:     rule.Resource.Type,
				Name:     rule.Resource.Name,
				Endpoint: rule.Resource.Endpoint,
			},
		},
		Options: &pb.Options{
			Namespace: s.Options().Issuer,
		},
	}, s.callOpts()...)

	return err
}

// Revoke access to a resource
func (s *srv) Revoke(rule *auth.Rule) error {
	_, err := s.rules.Delete(context.DefaultContext, &pb.DeleteRequest{
		Id: rule.ID, Options: &pb.Options{
			Namespace: s.Options().Issuer,
		},
	}, s.callOpts()...)

	return err
}

func (s *srv) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
	var options auth.RulesOptions
	for _, o := range opts {
		o(&options)
	}
	if options.Context == nil {
		options.Context = context.DefaultContext
	}
	if len(options.Namespace) == 0 {
		options.Namespace = s.options.Issuer
	}

	callOpts := append(s.callOpts(), cache.CallExpiry(time.Second*30))
	rsp, err := s.rules.List(context.DefaultContext, &pb.ListRequest{
		Options: &pb.Options{Namespace: options.Namespace},
	}, callOpts...)
	if err != nil {
		return nil, err
	}

	rules := make([]*auth.Rule, len(rsp.Rules))
	for i, r := range rsp.Rules {
		rules[i] = serializeRule(r)
	}

	return rules, nil
}

// Verify an account has access to a resource
func (s *srv) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
	var options auth.VerifyOptions
	for _, o := range opts {
		o(&options)
	}

	rs, err := s.Rules(
		auth.RulesContext(options.Context),
		auth.RulesNamespace(options.Namespace),
	)
	if err != nil {
		return err
	}
	return rules.VerifyAccess(rs, acc, res, opts...)
}

// Inspect a token
func (s *srv) Inspect(token string) (*auth.Account, error) {
	// validate the request
	if len(token) == 0 {
		return nil, auth.ErrInvalidToken
	}

	// try to decode JWT locally and fall back to srv if an error occurs
	if len(strings.Split(token, ".")) == 3 && len(s.options.PublicKey) > 0 {
		return s.token.Inspect(token)
	}

	// the token is not a JWT or we do not have the keys to decode it,
	// fall back to the auth service
	rsp, err := s.auth.Inspect(context.DefaultContext, &pb.InspectRequest{
		Token: token, Options: &pb.Options{Namespace: s.Options().Issuer},
	}, s.callOpts()...)
	if err != nil {
		return nil, err
	}
	return serializeAccount(rsp.Account), nil
}

// Token generation using an account ID and secret
func (s *srv) Token(opts ...auth.TokenOption) (*auth.AccountToken, error) {
	options := auth.NewTokenOptions(opts...)
	if len(options.Issuer) == 0 {
		options.Issuer = s.options.Issuer
	}

	tok := options.RefreshToken
	if len(options.Secret) > 0 {
		tok = options.Secret
	}

	// we have the JWT private key and refresh accounts locally
	if len(s.options.PrivateKey) > 0 && len(strings.Split(tok, ".")) == 3 {
		acc, err := s.token.Inspect(tok)
		if err != nil {
			return nil, err
		}

		token, err := s.token.Generate(acc, token.WithExpiry(options.Expiry))
		if err != nil {
			return nil, err
		}

		return &auth.AccountToken{
			Expiry:       token.Expiry,
			AccessToken:  token.Token,
			RefreshToken: tok,
		}, nil
	}

	rsp, err := s.auth.Token(context.DefaultContext, &pb.TokenRequest{
		Id:           options.ID,
		Secret:       options.Secret,
		RefreshToken: options.RefreshToken,
		TokenExpiry:  int64(options.Expiry.Seconds()),
		Options: &pb.Options{
			Namespace: options.Issuer,
		},
	}, s.callOpts()...)
	if err != nil && errors.FromError(err).Detail == auth.ErrInvalidToken.Error() {
		return nil, auth.ErrInvalidToken
	} else if err != nil {
		return nil, err
	}

	return serializeToken(rsp.Token), nil
}

func serializeToken(t *pb.Token) *auth.AccountToken {
	return &auth.AccountToken{
		AccessToken:  t.AccessToken,
		RefreshToken: t.RefreshToken,
		Created:      time.Unix(t.Created, 0),
		Expiry:       time.Unix(t.Expiry, 0),
	}
}

func serializeAccount(a *pb.Account) *auth.Account {
	return &auth.Account{
		ID:       a.Id,
		Secret:   a.Secret,
		Issuer:   a.Issuer,
		Metadata: a.Metadata,
		Scopes:   a.Scopes,
		Name:     a.Name,
	}
}

func serializeRule(r *pb.Rule) *auth.Rule {
	var access auth.Access
	if r.Access == pb.Access_GRANTED {
		access = auth.AccessGranted
	} else {
		access = auth.AccessDenied
	}

	return &auth.Rule{
		ID:       r.Id,
		Scope:    r.Scope,
		Access:   access,
		Priority: r.Priority,
		Resource: &auth.Resource{
			Type:     r.Resource.Type,
			Name:     r.Resource.Name,
			Endpoint: r.Resource.Endpoint,
		},
	}
}

func (s *srv) callOpts() []client.CallOption {
	return []client.CallOption{
		client.WithAddress(s.options.Addrs...),
		client.WithAuthToken(),
	}
}

// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
	service := &srv{
		auth:    pb.NewAuthService("auth", client.DefaultClient),
		rules:   pb.NewRulesService("auth", client.DefaultClient),
		options: auth.NewOptions(opts...),
	}

	service.setupJWT()

	return service
}

func (s *srv) setupJWT() {
	tokenOpts := []token.Option{}

	// if we have a JWT public key passed as an option,
	// we can decode tokens with the type "JWT" locally
	// and not have to make an RPC call
	if key := s.options.PublicKey; len(key) > 0 {
		tokenOpts = append(tokenOpts, token.WithPublicKey(key))
	}

	// if we have a JWT private key passed as an option,
	// we can generate accounts locally and not have to make
	// an RPC call, this is used for micro clients such as
	// api, web, proxy.
	if key := s.options.PrivateKey; len(key) > 0 {
		tokenOpts = append(tokenOpts, token.WithPrivateKey(key))
	}

	s.token = jwt.NewTokenProvider(tokenOpts...)
}
<commit_msg>missing type (#1767)<commit_after>package client

import (
	"strings"
	"time"

	"github.com/micro/micro/v3/internal/auth/rules"
	"github.com/micro/micro/v3/internal/auth/token"
	"github.com/micro/micro/v3/internal/auth/token/jwt"
	pb "github.com/micro/micro/v3/proto/auth"
	"github.com/micro/micro/v3/service/auth"
	"github.com/micro/micro/v3/service/client"
	"github.com/micro/micro/v3/service/client/cache"
	"github.com/micro/micro/v3/service/context"
	"github.com/micro/micro/v3/service/errors"
)

// srv is the service implementation of the Auth interface
type srv struct {
	options auth.Options
	auth    pb.AuthService
	rules   pb.RulesService
	token   token.Provider
}

func (s *srv) String() string {
	return "service"
}

func (s *srv) Init(opts ...auth.Option) {
	for _, o := range opts {
		o(&s.options)
	}
	s.auth = pb.NewAuthService("auth", client.DefaultClient)
	s.rules = pb.NewRulesService("auth", client.DefaultClient)
	s.setupJWT()
}

func (s *srv) Options() auth.Options {
	return s.options
}

// Generate a new account
func (s *srv) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
	options := auth.NewGenerateOptions(opts...)
	if len(options.Issuer) == 0 {
		options.Issuer = s.options.Issuer
	}

	// we have the JWT private key and generate ourselves an account
	if len(s.options.PrivateKey) > 0 {
		acc := &auth.Account{
			ID:       id,
			Type:     options.Type,
			Scopes:   options.Scopes,
			Metadata: options.Metadata,
			Issuer:   options.Issuer,
		}

		tok, err := s.token.Generate(acc, token.WithExpiry(time.Hour*24*365))
		if err != nil {
			return nil, err
		}

		// when using JWTs, the account secret is the JWT's token. This
		// can be used as an argument in the Token method.
		acc.Secret = tok.Token
		return acc, nil
	}

	rsp, err := s.auth.Generate(context.DefaultContext, &pb.GenerateRequest{
		Id:       id,
		Type:     options.Type,
		Secret:   options.Secret,
		Scopes:   options.Scopes,
		Metadata: options.Metadata,
		Provider: options.Provider,
		Options: &pb.Options{
			Namespace: options.Issuer,
		},
		Name: options.Name,
	}, s.callOpts()...)
	if err != nil {
		return nil, err
	}

	return serializeAccount(rsp.Account), nil
}

// Grant access to a resource
func (s *srv) Grant(rule *auth.Rule) error {
	access := pb.Access_UNKNOWN
	if rule.Access == auth.AccessGranted {
		access = pb.Access_GRANTED
	} else if rule.Access == auth.AccessDenied {
		access = pb.Access_DENIED
	}

	_, err := s.rules.Create(context.DefaultContext, &pb.CreateRequest{
		Rule: &pb.Rule{
			Id:       rule.ID,
			Scope:    rule.Scope,
			Priority: rule.Priority,
			Access:   access,
			Resource: &pb.Resource{
				Type:     rule.Resource.Type,
				Name:     rule.Resource.Name,
				Endpoint: rule.Resource.Endpoint,
			},
		},
		Options: &pb.Options{
			Namespace: s.Options().Issuer,
		},
	}, s.callOpts()...)

	return err
}

// Revoke access to a resource
func (s *srv) Revoke(rule *auth.Rule) error {
	_, err := s.rules.Delete(context.DefaultContext, &pb.DeleteRequest{
		Id: rule.ID, Options: &pb.Options{
			Namespace: s.Options().Issuer,
		},
	}, s.callOpts()...)

	return err
}

func (s *srv) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
	var options auth.RulesOptions
	for _, o := range opts {
		o(&options)
	}
	if options.Context == nil {
		options.Context = context.DefaultContext
	}
	if len(options.Namespace) == 0 {
		options.Namespace = s.options.Issuer
	}

	callOpts := append(s.callOpts(), cache.CallExpiry(time.Second*30))
	rsp, err := s.rules.List(context.DefaultContext, &pb.ListRequest{
		Options: &pb.Options{Namespace: options.Namespace},
	}, callOpts...)
	if err != nil {
		return nil, err
	}

	rules := make([]*auth.Rule, len(rsp.Rules))
	for i, r := range rsp.Rules {
		rules[i] = serializeRule(r)
	}

	return rules, nil
}

// Verify an account has access to a resource
func (s *srv) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
	var options auth.VerifyOptions
	for _, o := range opts {
		o(&options)
	}

	rs, err := s.Rules(
		auth.RulesContext(options.Context),
		auth.RulesNamespace(options.Namespace),
	)
	if err != nil {
		return err
	}
	return rules.VerifyAccess(rs, acc, res, opts...)
}

// Inspect a token
func (s *srv) Inspect(token string) (*auth.Account, error) {
	// validate the request
	if len(token) == 0 {
		return nil, auth.ErrInvalidToken
	}

	// try to decode JWT locally and fall back to srv if an error occurs
	if len(strings.Split(token, ".")) == 3 && len(s.options.PublicKey) > 0 {
		return s.token.Inspect(token)
	}

	// the token is not a JWT or we do not have the keys to decode it,
	// fall back to the auth service
	rsp, err := s.auth.Inspect(context.DefaultContext, &pb.InspectRequest{
		Token: token, Options: &pb.Options{Namespace: s.Options().Issuer},
	}, s.callOpts()...)
	if err != nil {
		return nil, err
	}
	return serializeAccount(rsp.Account), nil
}

// Token generation using an account ID and secret
func (s *srv) Token(opts ...auth.TokenOption) (*auth.AccountToken, error) {
	options := auth.NewTokenOptions(opts...)
	if len(options.Issuer) == 0 {
		options.Issuer = s.options.Issuer
	}

	tok := options.RefreshToken
	if len(options.Secret) > 0 {
		tok = options.Secret
	}

	// we have the JWT private key and refresh accounts locally
	if len(s.options.PrivateKey) > 0 && len(strings.Split(tok, ".")) == 3 {
		acc, err := s.token.Inspect(tok)
		if err != nil {
			return nil, err
		}

		token, err := s.token.Generate(acc, token.WithExpiry(options.Expiry))
		if err != nil {
			return nil, err
		}

		return &auth.AccountToken{
			Expiry:       token.Expiry,
			AccessToken:  token.Token,
			RefreshToken: tok,
		}, nil
	}

	rsp, err := s.auth.Token(context.DefaultContext, &pb.TokenRequest{
		Id:           options.ID,
		Secret:       options.Secret,
		RefreshToken: options.RefreshToken,
		TokenExpiry:  int64(options.Expiry.Seconds()),
		Options: &pb.Options{
			Namespace: options.Issuer,
		},
	}, s.callOpts()...)
	if err != nil && errors.FromError(err).Detail == auth.ErrInvalidToken.Error() {
		return nil, auth.ErrInvalidToken
	} else if err != nil {
		return nil, err
	}

	return serializeToken(rsp.Token), nil
}

func serializeToken(t *pb.Token) *auth.AccountToken {
	return &auth.AccountToken{
		AccessToken:  t.AccessToken,
		RefreshToken: t.RefreshToken,
		Created:      time.Unix(t.Created, 0),
		Expiry:       time.Unix(t.Expiry, 0),
	}
}

func serializeAccount(a *pb.Account) *auth.Account {
	return &auth.Account{
		ID:       a.Id,
		Secret:   a.Secret,
		Issuer:   a.Issuer,
		Metadata: a.Metadata,
		Scopes:   a.Scopes,
		Name:     a.Name,
		Type:     a.Type,
	}
}

func serializeRule(r *pb.Rule) *auth.Rule {
	var access auth.Access
	if r.Access == pb.Access_GRANTED {
		access = auth.AccessGranted
	} else {
		access = auth.AccessDenied
	}

	return &auth.Rule{
		ID:       r.Id,
		Scope:    r.Scope,
		Access:   access,
		Priority: r.Priority,
		Resource: &auth.Resource{
			Type:     r.Resource.Type,
			Name:     r.Resource.Name,
			Endpoint: r.Resource.Endpoint,
		},
	}
}

func (s *srv) callOpts() []client.CallOption {
	return []client.CallOption{
		client.WithAddress(s.options.Addrs...),
		client.WithAuthToken(),
	}
}

// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
	service := &srv{
		auth:    pb.NewAuthService("auth", client.DefaultClient),
		rules:   pb.NewRulesService("auth", client.DefaultClient),
		options: auth.NewOptions(opts...),
	}

	service.setupJWT()

	return service
}

func (s *srv) setupJWT() {
	tokenOpts := []token.Option{}

	// if we have a JWT public key passed as an option,
	// we can decode tokens with the type "JWT" locally
	// and not have to make an RPC call
	if key := s.options.PublicKey; len(key) > 0 {
		tokenOpts = append(tokenOpts, token.WithPublicKey(key))
	}

	// if we have a JWT private key passed as an option,
	// we can generate accounts locally and not have to make
	// an RPC call, this is used for micro clients such as
	// api, web, proxy.
	if key := s.options.PrivateKey; len(key) > 0 {
		tokenOpts = append(tokenOpts, token.WithPrivateKey(key))
	}

	s.token = jwt.NewTokenProvider(tokenOpts...)
}
<|endoftext|>