<commit_before>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"bufio"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"time"
)

var (
	errNotTLS           = errors.New("Not a TLS connection")
	errNoPeerCerts      = errors.New("Client did not provide a certificate")
	handshakeTimeout, _ = time.ParseDuration("5s")
)

// Socket represents an IRC socket.
type Socket struct {
	Closed bool
	conn   net.Conn
	reader *bufio.Reader

	MaxSendQBytes uint64

	lineToSendExists chan bool
	linesToSend      []string
	linesToSendMutex sync.Mutex
}

// NewSocket returns a new Socket.
func NewSocket(conn net.Conn, maxSendQBytes uint64) Socket {
	return Socket{
		conn:             conn,
		reader:           bufio.NewReader(conn),
		MaxSendQBytes:    maxSendQBytes,
		lineToSendExists: make(chan bool),
	}
}

// Close stops a Socket from being able to send/receive any more data.
func (socket *Socket) Close() {
	socket.Closed = true

	// 'send data' to force close loop to happen
	socket.linesToSendMutex.Lock()
	socket.linesToSend = append(socket.linesToSend, "")
	socket.linesToSendMutex.Unlock()
	go socket.fillLineToSendExists()
}

// CertFP returns the fingerprint of the certificate provided by the client.
func (socket *Socket) CertFP() (string, error) {
	var tlsConn, isTLS = socket.conn.(*tls.Conn)
	if !isTLS {
		return "", errNotTLS
	}

	// ensure handehake is performed, and timeout after a few seconds
	tlsConn.SetDeadline(time.Now().Add(handshakeTimeout))
	err := tlsConn.Handshake()
	tlsConn.SetDeadline(time.Time{})

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

	peerCerts := tlsConn.ConnectionState().PeerCertificates
	if len(peerCerts) < 1 {
		return "", errNoPeerCerts
	}

	rawCert := sha256.Sum256(peerCerts[0].Raw)
	fingerprint := hex.EncodeToString(rawCert[:])

	return fingerprint, nil
}

// Read returns a single IRC line from a Socket.
func (socket *Socket) Read() (string, error) {
	if socket.Closed {
		return "", io.EOF
	}

	lineBytes, err := socket.reader.ReadBytes('\n')

	// convert bytes to string
	line := string(lineBytes[:])

	// read last message properly (such as ERROR/QUIT/etc), just fail next reads/writes
	if err == io.EOF {
		socket.Close()
	}

	if err == io.EOF && strings.TrimSpace(line) != "" {
		// don't do anything
	} else if err != nil {
		return "", err
	}

	return strings.TrimRight(line, "\r\n"), nil
}

// Write sends the given string out of Socket.
func (socket *Socket) Write(data string) error {
	if socket.Closed {
		return io.EOF
	}

	socket.linesToSendMutex.Lock()
	socket.linesToSend = append(socket.linesToSend, data)
	socket.linesToSendMutex.Unlock()
	go socket.fillLineToSendExists()

	return nil
}

// fillLineToSendExists only exists because you can't goroutine single statements.
func (socket *Socket) fillLineToSendExists() {
	socket.lineToSendExists <- true
}

// RunSocketWriter starts writing messages to the outgoing socket.
func (socket *Socket) RunSocketWriter() {
	var errOut bool
	for {
		// wait for new lines
		select {
		case <-socket.lineToSendExists:
			socket.linesToSendMutex.Lock()

			// check sendq
			var sendQBytes uint64
			for _, line := range socket.linesToSend {
				sendQBytes += uint64(len(line))
				if socket.MaxSendQBytes < sendQBytes {
					break
				}
			}
			if socket.MaxSendQBytes < sendQBytes {
				socket.conn.Write([]byte("\r\nERROR :SendQ Exceeded\r\n"))
				fmt.Println("SendQ exceeded, disconnected client")
				break
			}

			// get data
			data := socket.linesToSend[0]
			if len(socket.linesToSend) > 1 {
				socket.linesToSend = socket.linesToSend[1:]
			} else {
				socket.linesToSend = []string{}
			}

			socket.linesToSendMutex.Unlock()

			// write data
			if 0 < len(data) {
				_, err := socket.conn.Write([]byte(data))
				if err != nil {
					errOut = true
					fmt.Println(err.Error())
					break
				}
			}

			// check if we're closed
			if socket.Closed {
				break
			}
		}
		if errOut {
			// error out, bad stuff happened
			break
		}
	}
	//TODO(dan): empty socket.lineToSendExists queue
	socket.conn.Close()
	if !socket.Closed {
		socket.Closed = true
	}
}

// WriteLine writes the given line out of Socket.
func (socket *Socket) WriteLine(line string) error {
	return socket.Write(line + "\r\n")
}
<commit_msg>socket: Make closing work a little better<commit_after>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"bufio"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"time"
)

var (
	errNotTLS           = errors.New("Not a TLS connection")
	errNoPeerCerts      = errors.New("Client did not provide a certificate")
	handshakeTimeout, _ = time.ParseDuration("5s")
)

// Socket represents an IRC socket.
type Socket struct {
	Closed bool
	conn   net.Conn
	reader *bufio.Reader

	MaxSendQBytes uint64

	lineToSendExists chan bool
	linesToSend      []string
	linesToSendMutex sync.Mutex
}

// NewSocket returns a new Socket.
func NewSocket(conn net.Conn, maxSendQBytes uint64) Socket {
	return Socket{
		conn:             conn,
		reader:           bufio.NewReader(conn),
		MaxSendQBytes:    maxSendQBytes,
		lineToSendExists: make(chan bool),
	}
}

// Close stops a Socket from being able to send/receive any more data.
func (socket *Socket) Close() {
	socket.Closed = true

	// force close loop to happen
	go socket.fillLineToSendExists()
}

// CertFP returns the fingerprint of the certificate provided by the client.
func (socket *Socket) CertFP() (string, error) {
	var tlsConn, isTLS = socket.conn.(*tls.Conn)
	if !isTLS {
		return "", errNotTLS
	}

	// ensure handehake is performed, and timeout after a few seconds
	tlsConn.SetDeadline(time.Now().Add(handshakeTimeout))
	err := tlsConn.Handshake()
	tlsConn.SetDeadline(time.Time{})

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

	peerCerts := tlsConn.ConnectionState().PeerCertificates
	if len(peerCerts) < 1 {
		return "", errNoPeerCerts
	}

	rawCert := sha256.Sum256(peerCerts[0].Raw)
	fingerprint := hex.EncodeToString(rawCert[:])

	return fingerprint, nil
}

// Read returns a single IRC line from a Socket.
func (socket *Socket) Read() (string, error) {
	if socket.Closed {
		return "", io.EOF
	}

	lineBytes, err := socket.reader.ReadBytes('\n')

	// convert bytes to string
	line := string(lineBytes[:])

	// read last message properly (such as ERROR/QUIT/etc), just fail next reads/writes
	if err == io.EOF {
		socket.Close()
	}

	if err == io.EOF && strings.TrimSpace(line) != "" {
		// don't do anything
	} else if err != nil {
		return "", err
	}

	return strings.TrimRight(line, "\r\n"), nil
}

// Write sends the given string out of Socket.
func (socket *Socket) Write(data string) error {
	if socket.Closed {
		return io.EOF
	}

	socket.linesToSendMutex.Lock()
	socket.linesToSend = append(socket.linesToSend, data)
	socket.linesToSendMutex.Unlock()
	go socket.fillLineToSendExists()

	return nil
}

// fillLineToSendExists only exists because you can't goroutine single statements.
func (socket *Socket) fillLineToSendExists() {
	socket.lineToSendExists <- true
}

// RunSocketWriter starts writing messages to the outgoing socket.
func (socket *Socket) RunSocketWriter() {
	var errOut bool
	for {
		// wait for new lines
		select {
		case <-socket.lineToSendExists:
			socket.linesToSendMutex.Lock()

			// check if we're closed
			if socket.Closed {
				break
			}

			// check sendq
			var sendQBytes uint64
			for _, line := range socket.linesToSend {
				sendQBytes += uint64(len(line))
				if socket.MaxSendQBytes < sendQBytes {
					break
				}
			}
			if socket.MaxSendQBytes < sendQBytes {
				socket.conn.Write([]byte("\r\nERROR :SendQ Exceeded\r\n"))
				fmt.Println("SendQ exceeded, disconnected client")
				break
			}

			// get data
			data := socket.linesToSend[0]
			if len(socket.linesToSend) > 1 {
				socket.linesToSend = socket.linesToSend[1:]
			} else {
				socket.linesToSend = []string{}
			}

			socket.linesToSendMutex.Unlock()

			// write data
			if 0 < len(data) {
				_, err := socket.conn.Write([]byte(data))
				if err != nil {
					errOut = true
					fmt.Println(err.Error())
					break
				}
			}

			// check if we're closed
			if socket.Closed {
				break
			}
		}
		if errOut {
			// error out, bad stuff happened
			break
		}
	}
	//TODO(dan): empty socket.lineToSendExists queue
	socket.conn.Close()
	if !socket.Closed {
		socket.Closed = true
	}
}

// WriteLine writes the given line out of Socket.
func (socket *Socket) WriteLine(line string) error {
	return socket.Write(line + "\r\n")
}
<|endoftext|>


<commit_before>package termite

import (
	"bytes"
	"exec"
	"fmt"
	"github.com/hanwen/go-fuse/unionfs"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type WorkerTask struct {
	*WorkRequest
	*WorkResponse
	stdinConn net.Conn
	mirror    *Mirror
	cmd       *exec.Cmd
	taskInfo string
}

func (me *WorkerTask) Kill() {
	// TODO - racy.
	if me.cmd.Process != nil {
		pid := me.cmd.Process.Pid
		errNo := syscall.Kill(pid, syscall.SIGQUIT)
		log.Printf("Killed pid %d, result %d", pid, errNo)
	}
}

func (me *WorkerTask) String() string {
	return me.taskInfo
}

func (me *WorkResponse) resetClock() {
	me.LastTime = time.Nanoseconds()
}

func (me *WorkResponse) clock(name string) {
	t := time.Nanoseconds()
	me.Timings = append(me.Timings,
		Timing{name, 1.0e-6 * float64(t-me.LastTime)})
	me.LastTime = t
}

func (me *WorkerTask) clock(name string) {
	me.WorkResponse.clock(name)
}

func (me *WorkerTask) Run() os.Error {
	fuseFs, err := me.mirror.newFs(me)
	if err != nil {
		return err
	}

	me.resetClock()
	err = me.runInFuse(fuseFs)
	if me.mirror.considerReap(fuseFs, me) {
		me.WorkResponse.FileSet, me.WorkResponse.TaskIds = me.mirror.reapFuse(fuseFs)
	}

	return err
}

func (me *WorkerTask) runInFuse(fuseFs *workerFuseFs) os.Error {
	fuseFs.SetDebug(me.WorkRequest.Debug)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	me.cmd = exec.Command(me.WorkRequest.Binary,
		me.WorkRequest.Argv[1:]...)
	cmd := me.cmd

	cmd.Args[0] = me.WorkRequest.Argv[0]
	if os.Geteuid() == 0 {
		attr := &syscall.SysProcAttr{}
		attr.Credential = &syscall.Credential{
			Uid: uint32(me.mirror.daemon.Nobody.Uid),
			Gid: uint32(me.mirror.daemon.Nobody.Gid),
		}
		attr.Chroot = fuseFs.mount

		cmd.SysProcAttr = attr
		cmd.Dir = me.WorkRequest.Dir
	} else {
		cmd.Path = filepath.Join(fuseFs.mount, me.WorkRequest.Binary)
		cmd.Dir = filepath.Join(fuseFs.mount, me.WorkRequest.Dir)
	}

	cmd.Env = me.WorkRequest.Env
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if me.stdinConn != nil {
		cmd.Stdin = me.stdinConn
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	printCmd := fmt.Sprintf("%v", cmd.Args)
	if me.WorkRequest.Debug {
		printCmd = fmt.Sprintf("%v", cmd)
	}
	me.taskInfo = fmt.Sprintf("%v, dir %v, fuse FS %d",
		printCmd, cmd.Dir, fuseFs.id)
	err := cmd.Wait()

	waitMsg, ok := err.(*os.Waitmsg)
	if ok {
		me.WorkResponse.Exit = *waitMsg
		err = nil
	}

	// No waiting: if the process exited, we kill the connection.
	if me.stdinConn != nil {
		me.stdinConn.Close()
	}

	// We could use a connection here too, but this is simpler.
	me.WorkResponse.Stdout = stdout.String()
	me.WorkResponse.Stderr = stderr.String()

	me.clock("worker.runCommand")
	return err
}

// Sorts FileAttr such deletions come reversed before additions.

func (me *Mirror) fillReply(ufs *unionfs.MemUnionFs) *FileSet {
	yield := ufs.Reap()
	wrRoot := strings.TrimLeft(me.writableRoot, "/")
	cache := me.daemon.contentCache

	files := []*FileAttr{}
	reapedHashes := map[string]string{}
	for path, v := range yield {
		f := &FileAttr{
			Path: filepath.Join(wrRoot, path),
		}

		f.FileInfo = v.FileInfo
		f.Link = v.Link
		if f.FileInfo != nil && f.FileInfo.IsRegular() {
			contentPath := filepath.Join(wrRoot, v.Original)
			if v.Original != "" && v.Original != contentPath {
				fa := me.rpcFs.attr.Get(contentPath)
				if fa.Hash == "" {
					log.Panicf("Contents for %q disappeared.", contentPath)
				}
				f.Hash = fa.Hash
			}
			if v.Backing != "" {
				f.Hash = reapedHashes[v.Backing]
				var err os.Error
				if f.Hash == "" {
					f.Hash, err = cache.DestructiveSavePath(v.Backing)
				}
				if err != nil {
					log.Fatalf("DestructiveSavePath fail %q: %v", v.Backing, err)
				} else {
					reapedHashes[v.Backing] = f.Hash
				}
			}
		}

		files = append(files, f)
	}
	fs := FileSet{files}
	ufs.Clear()
	fs.Sort()

	return &fs
}
<commit_msg>Reinstate forgotten line.<commit_after>package termite

import (
	"bytes"
	"exec"
	"fmt"
	"github.com/hanwen/go-fuse/unionfs"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type WorkerTask struct {
	*WorkRequest
	*WorkResponse
	stdinConn net.Conn
	mirror    *Mirror
	cmd       *exec.Cmd
	taskInfo string
}

func (me *WorkerTask) Kill() {
	// TODO - racy.
	if me.cmd.Process != nil {
		pid := me.cmd.Process.Pid
		errNo := syscall.Kill(pid, syscall.SIGQUIT)
		log.Printf("Killed pid %d, result %d", pid, errNo)
	}
}

func (me *WorkerTask) String() string {
	return me.taskInfo
}

func (me *WorkResponse) resetClock() {
	me.LastTime = time.Nanoseconds()
}

func (me *WorkResponse) clock(name string) {
	t := time.Nanoseconds()
	me.Timings = append(me.Timings,
		Timing{name, 1.0e-6 * float64(t-me.LastTime)})
	me.LastTime = t
}

func (me *WorkerTask) clock(name string) {
	me.WorkResponse.clock(name)
}

func (me *WorkerTask) Run() os.Error {
	fuseFs, err := me.mirror.newFs(me)
	if err != nil {
		return err
	}

	me.resetClock()
	err = me.runInFuse(fuseFs)
	defer me.mirror.returnFs(fuseFs)
	if me.mirror.considerReap(fuseFs, me) {
		me.WorkResponse.FileSet, me.WorkResponse.TaskIds = me.mirror.reapFuse(fuseFs)
	}

	return err
}

func (me *WorkerTask) runInFuse(fuseFs *workerFuseFs) os.Error {
	fuseFs.SetDebug(me.WorkRequest.Debug)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	me.cmd = exec.Command(me.WorkRequest.Binary,
		me.WorkRequest.Argv[1:]...)
	cmd := me.cmd

	cmd.Args[0] = me.WorkRequest.Argv[0]
	if os.Geteuid() == 0 {
		attr := &syscall.SysProcAttr{}
		attr.Credential = &syscall.Credential{
			Uid: uint32(me.mirror.daemon.Nobody.Uid),
			Gid: uint32(me.mirror.daemon.Nobody.Gid),
		}
		attr.Chroot = fuseFs.mount

		cmd.SysProcAttr = attr
		cmd.Dir = me.WorkRequest.Dir
	} else {
		cmd.Path = filepath.Join(fuseFs.mount, me.WorkRequest.Binary)
		cmd.Dir = filepath.Join(fuseFs.mount, me.WorkRequest.Dir)
	}

	cmd.Env = me.WorkRequest.Env
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if me.stdinConn != nil {
		cmd.Stdin = me.stdinConn
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	printCmd := fmt.Sprintf("%v", cmd.Args)
	if me.WorkRequest.Debug {
		printCmd = fmt.Sprintf("%v", cmd)
	}
	me.taskInfo = fmt.Sprintf("%v, dir %v, fuse FS %d",
		printCmd, cmd.Dir, fuseFs.id)
	err := cmd.Wait()

	waitMsg, ok := err.(*os.Waitmsg)
	if ok {
		me.WorkResponse.Exit = *waitMsg
		err = nil
	}

	// No waiting: if the process exited, we kill the connection.
	if me.stdinConn != nil {
		me.stdinConn.Close()
	}

	// We could use a connection here too, but this is simpler.
	me.WorkResponse.Stdout = stdout.String()
	me.WorkResponse.Stderr = stderr.String()

	me.clock("worker.runCommand")
	return err
}

// Sorts FileAttr such deletions come reversed before additions.

func (me *Mirror) fillReply(ufs *unionfs.MemUnionFs) *FileSet {
	yield := ufs.Reap()
	wrRoot := strings.TrimLeft(me.writableRoot, "/")
	cache := me.daemon.contentCache

	files := []*FileAttr{}
	reapedHashes := map[string]string{}
	for path, v := range yield {
		f := &FileAttr{
			Path: filepath.Join(wrRoot, path),
		}

		f.FileInfo = v.FileInfo
		f.Link = v.Link
		if f.FileInfo != nil && f.FileInfo.IsRegular() {
			contentPath := filepath.Join(wrRoot, v.Original)
			if v.Original != "" && v.Original != contentPath {
				fa := me.rpcFs.attr.Get(contentPath)
				if fa.Hash == "" {
					log.Panicf("Contents for %q disappeared.", contentPath)
				}
				f.Hash = fa.Hash
			}
			if v.Backing != "" {
				f.Hash = reapedHashes[v.Backing]
				var err os.Error
				if f.Hash == "" {
					f.Hash, err = cache.DestructiveSavePath(v.Backing)
				}
				if err != nil {
					log.Fatalf("DestructiveSavePath fail %q: %v", v.Backing, err)
				} else {
					reapedHashes[v.Backing] = f.Hash
				}
			}
		}

		files = append(files, f)
	}
	fs := FileSet{files}
	ufs.Clear()
	fs.Sort()

	return &fs
}
<|endoftext|>


<commit_before>package termite

import (
	"bytes"
	"fmt"
	"github.com/hanwen/termite/attr"
	"github.com/hanwen/termite/fs"
	"log"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type WorkerTask struct {
	*WorkRequest
	*WorkResponse
	stdinConn net.Conn
	mirror    *Mirror
	cmd       *exec.Cmd
	taskInfo  string
}

func (me *WorkerTask) Kill() {
	// TODO - racy.
	if me.cmd.Process != nil {
		pid := me.cmd.Process.Pid
		errNo := syscall.Kill(pid, syscall.SIGQUIT)
		log.Printf("Killed pid %d, result %d", pid, errNo)
	}
}

func (me *WorkerTask) String() string {
	return me.taskInfo
}

func (me *WorkResponse) resetClock() {
	me.LastTime = time.Nanoseconds()
}

func (me *WorkResponse) clock(name string) {
	t := time.Nanoseconds()
	me.Timings = append(me.Timings,
		Timing{name, 1.0e-6 * float64(t-me.LastTime)})
	me.LastTime = t
}

func (me *WorkerTask) clock(name string) {
	me.WorkResponse.clock(name)
}

func (me *WorkerTask) Run() error {
	fuseFs, err := me.mirror.newFs(me)
	if err != nil {
		return err
	}

	me.resetClock()
	me.mirror.daemon.stats.Enter("fuse")
	err = me.runInFuse(fuseFs)
	me.mirror.daemon.stats.Exit("fuse")

	defer me.mirror.returnFs(fuseFs)

	me.mirror.daemon.stats.Enter("reap")
	if me.mirror.considerReap(fuseFs, me) {
		me.WorkResponse.FileSet, me.WorkResponse.TaskIds = me.mirror.reapFuse(fuseFs)
	}
	me.mirror.daemon.stats.Exit("reap")

	return err
}

func (me *WorkerTask) runInFuse(fuseFs *workerFuseFs) error {
	fuseFs.SetDebug(me.WorkRequest.Debug)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	// See /bin/true for the background of
	// /bin/true. http://code.google.com/p/go/issues/detail?id=2373
	me.cmd = &exec.Cmd{
		Path: me.WorkRequest.Binary,
		Args: me.WorkRequest.Argv,
	}
	cmd := me.cmd
	if os.Geteuid() == 0 {
		attr := &syscall.SysProcAttr{}
		attr.Credential = &syscall.Credential{
			Uid: uint32(me.mirror.daemon.options.User.Uid),
			Gid: uint32(me.mirror.daemon.options.User.Gid),
		}
		attr.Chroot = fuseFs.mount

		cmd.SysProcAttr = attr
		cmd.Dir = me.WorkRequest.Dir
	} else {
		cmd.Path = filepath.Join(fuseFs.mount, me.WorkRequest.Binary)
		cmd.Dir = filepath.Join(fuseFs.mount, me.WorkRequest.Dir)
	}

	cmd.Env = me.WorkRequest.Env
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if me.stdinConn != nil {
		cmd.Stdin = me.stdinConn
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	printCmd := fmt.Sprintf("%v", cmd.Args)
	if me.WorkRequest.Debug {
		printCmd = fmt.Sprintf("%v", cmd)
	}
	me.taskInfo = fmt.Sprintf("%v, dir %v, fuse FS %v",
		printCmd, cmd.Dir, fuseFs.id)
	err := cmd.Wait()

	waitMsg, ok := err.(*exec.ExitError)
	if ok {
		me.WorkResponse.Exit = *waitMsg.Waitmsg
		err = nil
	}

	// No waiting: if the process exited, we kill the connection.
	if me.stdinConn != nil {
		me.stdinConn.Close()
	}

	// We could use a connection here too, but this is simpler.
	me.WorkResponse.Stdout = stdout.String()
	me.WorkResponse.Stderr = stderr.String()

	me.clock("worker.runCommand")
	return err
}

// Sorts FileAttr such deletions come reversed before additions.

func (me *Mirror) fillReply(ufs *fs.MemUnionFs) *attr.FileSet {
	yield := ufs.Reap()
	wrRoot := strings.TrimLeft(me.writableRoot, "/")
	cache := me.daemon.contentCache

	files := []*attr.FileAttr{}
	reapedHashes := map[string]string{}
	for path, v := range yield {
		f := &attr.FileAttr{
			Path: filepath.Join(wrRoot, path),
		}

		f.FileInfo = v.FileInfo
		f.Link = v.Link
		if f.FileInfo != nil && f.FileInfo.IsRegular() {
			contentPath := filepath.Join(wrRoot, v.Original)
			if v.Original != "" && v.Original != contentPath {
				fa := me.rpcFs.attr.Get(contentPath)
				if fa.Hash == "" {
					log.Panicf("Contents for %q disappeared.", contentPath)
				}
				f.Hash = fa.Hash
			}
			if v.Backing != "" {
				f.Hash = reapedHashes[v.Backing]
				var err error
				if f.Hash == "" {
					f.Hash, err = cache.DestructiveSavePath(v.Backing)
				}
				if err != nil {
					log.Fatalf("DestructiveSavePath fail %q: %v", v.Backing, err)
				} else {
					reapedHashes[v.Backing] = f.Hash
				}
			}
		}

		files = append(files, f)
	}
	fs := attr.FileSet{Files: files}
	fs.Sort()

	return &fs
}
<commit_msg>Fix broken printf.<commit_after>package termite

import (
	"bytes"
	"fmt"
	"github.com/hanwen/termite/attr"
	"github.com/hanwen/termite/fs"
	"log"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type WorkerTask struct {
	*WorkRequest
	*WorkResponse
	stdinConn net.Conn
	mirror    *Mirror
	cmd       *exec.Cmd
	taskInfo  string
}

func (me *WorkerTask) Kill() {
	// TODO - racy.
	if me.cmd.Process != nil {
		pid := me.cmd.Process.Pid
		err := syscall.Kill(pid, syscall.SIGQUIT)
		log.Printf("Killed pid %d, result %v", pid, err)
	}
}

func (me *WorkerTask) String() string {
	return me.taskInfo
}

func (me *WorkResponse) resetClock() {
	me.LastTime = time.Nanoseconds()
}

func (me *WorkResponse) clock(name string) {
	t := time.Nanoseconds()
	me.Timings = append(me.Timings,
		Timing{name, 1.0e-6 * float64(t-me.LastTime)})
	me.LastTime = t
}

func (me *WorkerTask) clock(name string) {
	me.WorkResponse.clock(name)
}

func (me *WorkerTask) Run() error {
	fuseFs, err := me.mirror.newFs(me)
	if err != nil {
		return err
	}

	me.resetClock()
	me.mirror.daemon.stats.Enter("fuse")
	err = me.runInFuse(fuseFs)
	me.mirror.daemon.stats.Exit("fuse")

	defer me.mirror.returnFs(fuseFs)

	me.mirror.daemon.stats.Enter("reap")
	if me.mirror.considerReap(fuseFs, me) {
		me.WorkResponse.FileSet, me.WorkResponse.TaskIds = me.mirror.reapFuse(fuseFs)
	}
	me.mirror.daemon.stats.Exit("reap")

	return err
}

func (me *WorkerTask) runInFuse(fuseFs *workerFuseFs) error {
	fuseFs.SetDebug(me.WorkRequest.Debug)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	// See /bin/true for the background of
	// /bin/true. http://code.google.com/p/go/issues/detail?id=2373
	me.cmd = &exec.Cmd{
		Path: me.WorkRequest.Binary,
		Args: me.WorkRequest.Argv,
	}
	cmd := me.cmd
	if os.Geteuid() == 0 {
		attr := &syscall.SysProcAttr{}
		attr.Credential = &syscall.Credential{
			Uid: uint32(me.mirror.daemon.options.User.Uid),
			Gid: uint32(me.mirror.daemon.options.User.Gid),
		}
		attr.Chroot = fuseFs.mount

		cmd.SysProcAttr = attr
		cmd.Dir = me.WorkRequest.Dir
	} else {
		cmd.Path = filepath.Join(fuseFs.mount, me.WorkRequest.Binary)
		cmd.Dir = filepath.Join(fuseFs.mount, me.WorkRequest.Dir)
	}

	cmd.Env = me.WorkRequest.Env
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if me.stdinConn != nil {
		cmd.Stdin = me.stdinConn
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	printCmd := fmt.Sprintf("%v", cmd.Args)
	if me.WorkRequest.Debug {
		printCmd = fmt.Sprintf("%v", cmd)
	}
	me.taskInfo = fmt.Sprintf("%v, dir %v, fuse FS %v",
		printCmd, cmd.Dir, fuseFs.id)
	err := cmd.Wait()

	waitMsg, ok := err.(*exec.ExitError)
	if ok {
		me.WorkResponse.Exit = *waitMsg.Waitmsg
		err = nil
	}

	// No waiting: if the process exited, we kill the connection.
	if me.stdinConn != nil {
		me.stdinConn.Close()
	}

	// We could use a connection here too, but this is simpler.
	me.WorkResponse.Stdout = stdout.String()
	me.WorkResponse.Stderr = stderr.String()

	me.clock("worker.runCommand")
	return err
}

// Sorts FileAttr such deletions come reversed before additions.

func (me *Mirror) fillReply(ufs *fs.MemUnionFs) *attr.FileSet {
	yield := ufs.Reap()
	wrRoot := strings.TrimLeft(me.writableRoot, "/")
	cache := me.daemon.contentCache

	files := []*attr.FileAttr{}
	reapedHashes := map[string]string{}
	for path, v := range yield {
		f := &attr.FileAttr{
			Path: filepath.Join(wrRoot, path),
		}

		f.FileInfo = v.FileInfo
		f.Link = v.Link
		if f.FileInfo != nil && f.FileInfo.IsRegular() {
			contentPath := filepath.Join(wrRoot, v.Original)
			if v.Original != "" && v.Original != contentPath {
				fa := me.rpcFs.attr.Get(contentPath)
				if fa.Hash == "" {
					log.Panicf("Contents for %q disappeared.", contentPath)
				}
				f.Hash = fa.Hash
			}
			if v.Backing != "" {
				f.Hash = reapedHashes[v.Backing]
				var err error
				if f.Hash == "" {
					f.Hash, err = cache.DestructiveSavePath(v.Backing)
				}
				if err != nil {
					log.Fatalf("DestructiveSavePath fail %q: %v", v.Backing, err)
				} else {
					reapedHashes[v.Backing] = f.Hash
				}
			}
		}

		files = append(files, f)
	}
	fs := attr.FileSet{Files: files}
	fs.Sort()

	return &fs
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/realglobe-Inc/edo/util"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"github.com/realglobe-Inc/go-lib-rg/file"
	"github.com/realglobe-Inc/go-lib-rg/rglog/level"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type parameters struct {
	// 画面表示ログ。
	consLv level.Level

	// 追加ログ。
	logType string
	logLv   level.Level

	// ファイルログ。
	logPath string

	// fluentd ログ。
	fluAddr string
	fluTag  string

	// サービス検索。
	taExpType string

	// ファイルベースサービス検索。
	taExpPath string

	// Web ベースサービス検索。
	taExpAddr string

	// mongo サービス検索。
	taExpUrl  string
	taExpDb   string
	taExpColl string

	// 公開鍵レジストリ。
	taKeyRegType string

	// ファイルベース公開鍵レジストリ。
	taKeyRegPath string

	// Web ベース公開鍵レジストリ。
	taKeyRegAddr string

	// mongo 公開鍵レジストリ。
	taKeyRegUrl  string
	taKeyRegDb   string
	taKeyRegColl string

	// ユーザー名索引。
	usrNameIdxType string

	// ファイルベースユーザー名索引。
	usrNameIdxPath string

	// mongo ユーザー名索引。
	usrNameIdxUrl  string
	usrNameIdxDb   string
	usrNameIdxColl string

	// ユーザー属性レジストリ。
	usrAttrRegType string

	// ファイルベースユーザー属性レジストリ。
	usrAttrRegPath string

	// mongo ユーザー属性レジストリ。
	usrAttrRegUrl  string
	usrAttrRegDb   string
	usrAttrRegColl string

	// セッション管理。
	sessContType string

	// ファイルベースセッション管理。
	sessContPath string

	// mongo セッション管理。
	sessContUrl  string
	sessContDb   string
	sessContColl string

	// アクセストークン発行用コード管理。
	codeContType string

	// ファイルベースアクセストークン発行用コード管理。
	codeContPath string

	// mongo アクセストークン発行用コード管理。
	codeContUrl  string
	codeContDb   string
	codeContColl string

	// アクセストークン管理。
	accTokenContType string

	// ファイルベースアクセストークン管理。
	accTokenContPath string

	// mongo アクセストークン管理。
	accTokenContUrl  string
	accTokenContDb   string
	accTokenContColl string

	// ソケット。
	socType string

	// UNIX ソケット。
	socPath string

	// TCP ソケット。
	socPort int

	// プロトコル。
	protType string

	// 無通信での認証済みセッションの有効期間。
	maxSessExpiDur time.Duration // デフォルトかつ最大。

	// アクセストークン発行用コードの有効期間。
	codeExpiDur time.Duration

	// アクセストークンの有効期間。
	accTokenExpiDur    time.Duration // デフォルト。
	maxAccTokenExpiDur time.Duration
}

func parseParameters(args ...string) (param *parameters, err error) {

	const label = "id-provider"

	flags := util.NewFlagSet("edo-"+label+" parameters", flag.ExitOnError)
	flags.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:")
		fmt.Fprintln(os.Stderr, "  "+args[0]+" [{FLAG}...]")
		fmt.Fprintln(os.Stderr, "FLAG:")
		flags.PrintDefaults()
	}

	param = &parameters{}

	flags.Var(level.Var(&param.consLv, level.INFO), "consLv", "Console log level.")
	flags.StringVar(&param.logType, "logType", "", "Extra log type.")
	flags.Var(level.Var(&param.logLv, level.ALL), "logLv", "Extra log level.")
	flags.StringVar(&param.logPath, "logPath", filepath.Join(os.TempDir(), "edo-"+label+".log"), "File log path.")
	flags.StringVar(&param.fluAddr, "fluAddr", "localhost:24224", "fluentd address.")
	flags.StringVar(&param.fluTag, "fluTag", "edo."+label, "fluentd tag.")

	flags.StringVar(&param.taExpType, "taExpType", "web", "TA explorer type.")
	flags.StringVar(&param.taExpPath, "taExpPath", filepath.Join("sandbox", "service-expolorer"), "TA explorer directory.")
	flags.StringVar(&param.taExpAddr, "taExpAddr", "http://localhost:16034", "TA explorer address.")
	flags.StringVar(&param.taExpUrl, "taExpUrl", "localhost", "TA explorer address.")
	flags.StringVar(&param.taExpDb, "taExpDb", "edo", "TA explorer database name.")
	flags.StringVar(&param.taExpColl, "taExpColl", "ta-explorer", "TA explorer collection name.")

	flags.StringVar(&param.taKeyRegType, "taKeyRegType", "web", "TA key provider type.")
	flags.StringVar(&param.taKeyRegPath, "taKeyRegPath", filepath.Join("sandbox", "ta-key-provider"), "TA key provider directory.")
	flags.StringVar(&param.taKeyRegAddr, "taKeyRegAddr", "http://localhost:16033", "TA key provider address.")
	flags.StringVar(&param.taKeyRegUrl, "taKeyRegUrl", "localhost", "TA key provider address.")
	flags.StringVar(&param.taKeyRegDb, "taKeyRegDb", "edo", "TA key provider database name.")
	flags.StringVar(&param.taKeyRegColl, "taKeyRegColl", "ta-key-provider", "TA key provider collection name.")

	flags.StringVar(&param.usrNameIdxType, "usrNameIdxType", "mongo", "Username index type.")
	flags.StringVar(&param.usrNameIdxPath, "usrNameIdxPath", filepath.Join("sandbox", "user-name-index"), "Username index directory.")
	flags.StringVar(&param.usrNameIdxUrl, "usrNameIdxUrl", "localhost", "Username index address.")
	flags.StringVar(&param.usrNameIdxDb, "usrNameIdxDb", "edo", "Username index database name.")
	flags.StringVar(&param.usrNameIdxColl, "usrNameIdxColl", "user-name-index", "Username index collection name.")

	flags.StringVar(&param.usrAttrRegType, "usrAttrRegType", "mongo", "User attribute registry type.")
	flags.StringVar(&param.usrAttrRegPath, "usrAttrRegPath", filepath.Join("sandbox", "user-attribute-registry"), "User attribute registry directory.")
	flags.StringVar(&param.usrAttrRegUrl, "usrAttrRegUrl", "localhost", "User attribute registry address.")
	flags.StringVar(&param.usrAttrRegDb, "usrAttrRegDb", "edo", "User attribute registry database name.")
	flags.StringVar(&param.usrAttrRegColl, "usrAttrRegColl", "user-attribute-registry", "User attribute registry collection name.")

	flags.StringVar(&param.sessContType, "sessContType", "mongo", "Session container lister type.")
	flags.StringVar(&param.sessContPath, "sessContPath", filepath.Join("sandbox", "session-container"), "Session container lister directory.")
	flags.StringVar(&param.sessContUrl, "sessContUrl", "localhost", "Session container lister address.")
	flags.StringVar(&param.sessContDb, "sessContDb", "edo", "Session container lister database name.")
	flags.StringVar(&param.sessContColl, "sessContColl", "session-container", "Session container lister collection name.")

	flags.StringVar(&param.codeContType, "codeContType", "mongo", "Code container lister type.")
	flags.StringVar(&param.codeContPath, "codeContPath", filepath.Join("sandbox", "code-container"), "Code container lister directory.")
	flags.StringVar(&param.codeContUrl, "codeContUrl", "localhost", "Code container lister address.")
	flags.StringVar(&param.codeContDb, "codeContDb", "edo", "Code container lister database name.")
	flags.StringVar(&param.codeContColl, "codeContColl", "code-container", "Code container lister collection name.")

	flags.StringVar(&param.accTokenContType, "accTokenContType", "mongo", "Access token container lister type.")
	flags.StringVar(&param.accTokenContPath, "accTokenContPath", filepath.Join("sandbox", "access-token-container"), "Access token container lister directory.")
	flags.StringVar(&param.accTokenContUrl, "accTokenContUrl", "localhost", "Access token container lister address.")
	flags.StringVar(&param.accTokenContDb, "accTokenContDb", "edo", "Access token container lister database name.")
	flags.StringVar(&param.accTokenContColl, "accTokenContColl", "access-token-container", "Access token container lister collection name.")

	flags.StringVar(&param.socType, "socType", "tcp", "Socket type.")
	flags.StringVar(&param.socPath, "socPath", filepath.Join(os.TempDir(), "edo-"+label), "UNIX socket path.")
	flags.IntVar(&param.socPort, "socPort", 16040, "TCP socket port.")

	flags.StringVar(&param.protType, "protType", "http", "Protocol type.")

	flags.DurationVar(&param.maxSessExpiDur, "maxSessExpiDur", 24*time.Hour, "Max session expiration duration.")
	flags.DurationVar(&param.codeExpiDur, "codeExpiDur", 10*time.Minute, "Code expiration duration.")
	flags.DurationVar(&param.accTokenExpiDur, "accTokenExpiDur", 24*time.Hour, "Default access token expiration duration.")
	flags.DurationVar(&param.maxAccTokenExpiDur, "maxAccTokenExpiDur", 30*24*time.Hour, "Max access token expiration duration.")

	var config string
	flags.StringVar(&config, "f", "", "Config file path.")

	// 実行引数を読んで、設定ファイルを指定させてから、
	// 設定ファイルを読んで、また実行引数を読む。
	flags.Parse(args[1:])
	if config != "" {
		if exist, err := file.IsExist(config); err != nil {
			return nil, erro.Wrap(err)
		} else if !exist {
			log.Warn("Config file " + config + " is not exist.")
		} else {
			buff, err := ioutil.ReadFile(config)
			if err != nil {
				return nil, erro.Wrap(err)
			}
			flags.CompleteParse(strings.Fields(string(buff)))
		}
	}
	flags.Parse(args[1:])

	if l := len(flags.Args()); l > 0 {
		log.Warn("Ignore extra parameters ", flags.Args(), ".")
	}

	return param, nil
}
<commit_msg>ログファイルのデフォルトパスを変更<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/realglobe-Inc/edo/util"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"github.com/realglobe-Inc/go-lib-rg/file"
	"github.com/realglobe-Inc/go-lib-rg/rglog/level"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type parameters struct {
	// 画面表示ログ。
	consLv level.Level

	// 追加ログ。
	logType string
	logLv   level.Level

	// ファイルログ。
	logPath string

	// fluentd ログ。
	fluAddr string
	fluTag  string

	// サービス検索。
	taExpType string

	// ファイルベースサービス検索。
	taExpPath string

	// Web ベースサービス検索。
	taExpAddr string

	// mongo サービス検索。
	taExpUrl  string
	taExpDb   string
	taExpColl string

	// 公開鍵レジストリ。
	taKeyRegType string

	// ファイルベース公開鍵レジストリ。
	taKeyRegPath string

	// Web ベース公開鍵レジストリ。
	taKeyRegAddr string

	// mongo 公開鍵レジストリ。
	taKeyRegUrl  string
	taKeyRegDb   string
	taKeyRegColl string

	// ユーザー名索引。
	usrNameIdxType string

	// ファイルベースユーザー名索引。
	usrNameIdxPath string

	// mongo ユーザー名索引。
	usrNameIdxUrl  string
	usrNameIdxDb   string
	usrNameIdxColl string

	// ユーザー属性レジストリ。
	usrAttrRegType string

	// ファイルベースユーザー属性レジストリ。
	usrAttrRegPath string

	// mongo ユーザー属性レジストリ。
	usrAttrRegUrl  string
	usrAttrRegDb   string
	usrAttrRegColl string

	// セッション管理。
	sessContType string

	// ファイルベースセッション管理。
	sessContPath string

	// mongo セッション管理。
	sessContUrl  string
	sessContDb   string
	sessContColl string

	// アクセストークン発行用コード管理。
	codeContType string

	// ファイルベースアクセストークン発行用コード管理。
	codeContPath string

	// mongo アクセストークン発行用コード管理。
	codeContUrl  string
	codeContDb   string
	codeContColl string

	// アクセストークン管理。
	accTokenContType string

	// ファイルベースアクセストークン管理。
	accTokenContPath string

	// mongo アクセストークン管理。
	accTokenContUrl  string
	accTokenContDb   string
	accTokenContColl string

	// ソケット。
	socType string

	// UNIX ソケット。
	socPath string

	// TCP ソケット。
	socPort int

	// プロトコル。
	protType string

	// 無通信での認証済みセッションの有効期間。
	maxSessExpiDur time.Duration // デフォルトかつ最大。

	// アクセストークン発行用コードの有効期間。
	codeExpiDur time.Duration

	// アクセストークンの有効期間。
	accTokenExpiDur    time.Duration // デフォルト。
	maxAccTokenExpiDur time.Duration
}

func parseParameters(args ...string) (param *parameters, err error) {

	const label = "id-provider"

	flags := util.NewFlagSet("edo-"+label+" parameters", flag.ExitOnError)
	flags.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:")
		fmt.Fprintln(os.Stderr, "  "+args[0]+" [{FLAG}...]")
		fmt.Fprintln(os.Stderr, "FLAG:")
		flags.PrintDefaults()
	}

	param = &parameters{}

	flags.Var(level.Var(&param.consLv, level.INFO), "consLv", "Console log level.")
	flags.StringVar(&param.logType, "logType", "", "Extra log type.")
	flags.Var(level.Var(&param.logLv, level.ALL), "logLv", "Extra log level.")
	flags.StringVar(&param.logPath, "logPath", filepath.Join(os.TempDir(), "edo", label+".log"), "File log path.")
	flags.StringVar(&param.fluAddr, "fluAddr", "localhost:24224", "fluentd address.")
	flags.StringVar(&param.fluTag, "fluTag", "edo."+label, "fluentd tag.")

	flags.StringVar(&param.taExpType, "taExpType", "web", "TA explorer type.")
	flags.StringVar(&param.taExpPath, "taExpPath", filepath.Join("sandbox", "service-expolorer"), "TA explorer directory.")
	flags.StringVar(&param.taExpAddr, "taExpAddr", "http://localhost:16034", "TA explorer address.")
	flags.StringVar(&param.taExpUrl, "taExpUrl", "localhost", "TA explorer address.")
	flags.StringVar(&param.taExpDb, "taExpDb", "edo", "TA explorer database name.")
	flags.StringVar(&param.taExpColl, "taExpColl", "ta-explorer", "TA explorer collection name.")

	flags.StringVar(&param.taKeyRegType, "taKeyRegType", "web", "TA key provider type.")
	flags.StringVar(&param.taKeyRegPath, "taKeyRegPath", filepath.Join("sandbox", "ta-key-provider"), "TA key provider directory.")
	flags.StringVar(&param.taKeyRegAddr, "taKeyRegAddr", "http://localhost:16033", "TA key provider address.")
	flags.StringVar(&param.taKeyRegUrl, "taKeyRegUrl", "localhost", "TA key provider address.")
	flags.StringVar(&param.taKeyRegDb, "taKeyRegDb", "edo", "TA key provider database name.")
	flags.StringVar(&param.taKeyRegColl, "taKeyRegColl", "ta-key-provider", "TA key provider collection name.")

	flags.StringVar(&param.usrNameIdxType, "usrNameIdxType", "mongo", "Username index type.")
	flags.StringVar(&param.usrNameIdxPath, "usrNameIdxPath", filepath.Join("sandbox", "user-name-index"), "Username index directory.")
	flags.StringVar(&param.usrNameIdxUrl, "usrNameIdxUrl", "localhost", "Username index address.")
	flags.StringVar(&param.usrNameIdxDb, "usrNameIdxDb", "edo", "Username index database name.")
	flags.StringVar(&param.usrNameIdxColl, "usrNameIdxColl", "user-name-index", "Username index collection name.")

	flags.StringVar(&param.usrAttrRegType, "usrAttrRegType", "mongo", "User attribute registry type.")
	flags.StringVar(&param.usrAttrRegPath, "usrAttrRegPath", filepath.Join("sandbox", "user-attribute-registry"), "User attribute registry directory.")
	flags.StringVar(&param.usrAttrRegUrl, "usrAttrRegUrl", "localhost", "User attribute registry address.")
	flags.StringVar(&param.usrAttrRegDb, "usrAttrRegDb", "edo", "User attribute registry database name.")
	flags.StringVar(&param.usrAttrRegColl, "usrAttrRegColl", "user-attribute-registry", "User attribute registry collection name.")

	flags.StringVar(&param.sessContType, "sessContType", "mongo", "Session container lister type.")
	flags.StringVar(&param.sessContPath, "sessContPath", filepath.Join("sandbox", "session-container"), "Session container lister directory.")
	flags.StringVar(&param.sessContUrl, "sessContUrl", "localhost", "Session container lister address.")
	flags.StringVar(&param.sessContDb, "sessContDb", "edo", "Session container lister database name.")
	flags.StringVar(&param.sessContColl, "sessContColl", "session-container", "Session container lister collection name.")

	flags.StringVar(&param.codeContType, "codeContType", "mongo", "Code container lister type.")
	flags.StringVar(&param.codeContPath, "codeContPath", filepath.Join("sandbox", "code-container"), "Code container lister directory.")
	flags.StringVar(&param.codeContUrl, "codeContUrl", "localhost", "Code container lister address.")
	flags.StringVar(&param.codeContDb, "codeContDb", "edo", "Code container lister database name.")
	flags.StringVar(&param.codeContColl, "codeContColl", "code-container", "Code container lister collection name.")

	flags.StringVar(&param.accTokenContType, "accTokenContType", "mongo", "Access token container lister type.")
	flags.StringVar(&param.accTokenContPath, "accTokenContPath", filepath.Join("sandbox", "access-token-container"), "Access token container lister directory.")
	flags.StringVar(&param.accTokenContUrl, "accTokenContUrl", "localhost", "Access token container lister address.")
	flags.StringVar(&param.accTokenContDb, "accTokenContDb", "edo", "Access token container lister database name.")
	flags.StringVar(&param.accTokenContColl, "accTokenContColl", "access-token-container", "Access token container lister collection name.")

	flags.StringVar(&param.socType, "socType", "tcp", "Socket type.")
	flags.StringVar(&param.socPath, "socPath", filepath.Join(os.TempDir(), "edo-"+label), "UNIX socket path.")
	flags.IntVar(&param.socPort, "socPort", 16040, "TCP socket port.")

	flags.StringVar(&param.protType, "protType", "http", "Protocol type.")

	flags.DurationVar(&param.maxSessExpiDur, "maxSessExpiDur", 24*time.Hour, "Max session expiration duration.")
	flags.DurationVar(&param.codeExpiDur, "codeExpiDur", 10*time.Minute, "Code expiration duration.")
	flags.DurationVar(&param.accTokenExpiDur, "accTokenExpiDur", 24*time.Hour, "Default access token expiration duration.")
	flags.DurationVar(&param.maxAccTokenExpiDur, "maxAccTokenExpiDur", 30*24*time.Hour, "Max access token expiration duration.")

	var config string
	flags.StringVar(&config, "f", "", "Config file path.")

	// 実行引数を読んで、設定ファイルを指定させてから、
	// 設定ファイルを読んで、また実行引数を読む。
	flags.Parse(args[1:])
	if config != "" {
		if exist, err := file.IsExist(config); err != nil {
			return nil, erro.Wrap(err)
		} else if !exist {
			log.Warn("Config file " + config + " is not exist.")
		} else {
			buff, err := ioutil.ReadFile(config)
			if err != nil {
				return nil, erro.Wrap(err)
			}
			flags.CompleteParse(strings.Fields(string(buff)))
		}
	}
	flags.Parse(args[1:])

	if l := len(flags.Args()); l > 0 {
		log.Warn("Ignore extra parameters ", flags.Args(), ".")
	}

	return param, nil
}
<|endoftext|>


<commit_before>package controller

import (
	"fmt"
	"net/http"

	"ratings/models"
	"ratings/parser"
	"ratings/responses"

	"github.com/jinzhu/gorm"
	"github.com/labstack/echo"
)

type (
	frame struct {
		request *parser.Request
		context echo.Context
	}

	databases struct {
		read  *gorm.DB
		write *gorm.DB
	}
)

func errorResponse(context echo.Context) error {
	return responses.ErrorResponse(http.StatusInternalServerError, "", context)
}

/*
*
* App
*
 */
func getApp(db *gorm.DB, frame *frame) (*models.App, error) {
	result := models.GetApp(frame.request.App.Key, db)

	errorList := result.GetErrors()

	if len(errorList) > 0 || result.Value == nil {
		return &models.App{}, errorResponse(frame.context)
	}

	return result.Value.(*models.App), nil
}

/*
*
* Platform
*
 */
func getPlatform(db *gorm.DB, frame *frame) (*models.Platform, error) {
	result := models.GetPlatform(frame.request.Platform.Key, db)
	errorList := result.GetErrors()

	if len(errorList) > 0 || result.Value == nil {
		return &models.Platform{}, errorResponse(frame.context)
	}

	return result.Value.(*models.Platform), nil
}

/*
*
* Range
*
 */
func getRange(db *gorm.DB, frame *frame) (*models.Range, error) {
	result := models.GetRange(frame.request.Range, db)
	errorList := result.GetErrors()

	if len(errorList) > 0 || result.Value == nil {
		return &models.Range{}, errorResponse(frame.context)
	}

	return result.Value.(*models.Range), nil
}

/*
*
* AppUser
*
 */
func hasAppUser(request *parser.Request) bool {
	if request.User == nil {
		return false
	}

	appuser := request.User
	nameLength := len(appuser.Name)
	emailLength := len(appuser.Email)
	mibaIDLength := len(appuser.MiBAID)

	if nameLength == 0 || (emailLength == 0 && mibaIDLength == 0) {
		return false
	}

	return true
}

func getAppUser(dbs *databases, frame *frame) (*models.AppUser, error) {
	var getResult *gorm.DB

	if hasMibaID := len(frame.request.User.MiBAID); hasMibaID > 0 {
		getResult = models.GetAppUser(frame.request.User.MiBAID, dbs.read)
	} else {
		getResult = models.GetAppUserByEmail(frame.request.User.Email, dbs.read)
	}

	getErrorList := getResult.GetErrors()

	if getResult.RecordNotFound() {
		appuser := &models.AppUser{
			Name:   frame.request.User.Name,
			Email:  frame.request.User.Email,
			MiBAID: frame.request.User.MiBAID}

		createResult := models.CreateAppUser(appuser, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 {
			return &models.AppUser{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.AppUser), nil
	} else if len(getErrorList) > 0 {
		return &models.AppUser{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.AppUser), nil
}

func attachAppUser(rating *models.Rating, dbs *databases, frame *frame) error {
	appUser, err := getAppUser(dbs, frame)

	if err == nil {
		rating.AppUserID = appUser.ID
	}

	return err
}

/*
*
* Browser
*
 */
func hasBrowser(request *parser.Request) bool {
	if request.Browser == nil {
		return false
	}

	return true
}

func getBrowser(dbs *databases, frame *frame) (*models.Browser, error) {
	getResult := models.GetBrowser(frame.request.Browser.Name, dbs.read)
	getErrorList := getResult.GetErrors()

	if getResult.RecordNotFound() {
		browser := &models.Browser{Name: frame.request.Browser.Name}
		createResult := models.CreateBrowser(browser, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 || createResult.Value == nil {
			return &models.Browser{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.Browser), nil
	} else if len(getErrorList) > 0 || getResult.Value == nil {
		return &models.Browser{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.Browser), nil
}

func attachBrowser(rating *models.Rating, dbs *databases, frame *frame) error {
	browser, err := getBrowser(dbs, frame)

	if err == nil {
		rating.BrowserID = browser.ID
		rating.BrowserVersion = frame.request.Browser.Version

		return err
	}

	return nil
}

/*
*
* Device
*
 */
func getDevice(brand *models.Brand, platform *models.Platform, dbs *databases, frame *frame) (*models.Device, error) {
	deviceName := frame.request.Device.Name
	screenWidth := frame.request.Device.Screen.Width
	screenHeight := frame.request.Device.Screen.Height

	if frame.request.Device.Name == "Desktop" {
		deviceName = fmt.Sprintf("Desktop %dx%d", screenWidth, screenHeight)
	}

	getResult := models.GetDevice(deviceName, dbs.read)
	getErrorList := getResult.GetErrors()

	var device *models.Device

	if getResult.RecordNotFound() {
		device = &models.Device{
			Name:         deviceName,
			ScreenWidth:  frame.request.Device.Screen.Width,
			ScreenHeight: frame.request.Device.Screen.Height,
			PPI:          frame.request.Device.Screen.PPI,
			PlatformID:   platform.ID}

		if brand != nil {
			device.BrandID = brand.ID
		}
	}

	if result, ok := getResult.Value.(*models.Device); (ok && brand != nil) && (result.BrandID != brand.ID) {
		checkDeviceName := fmt.Sprintf("%v (%v)", deviceName, brand.Name)
		checkGetResult := models.GetDevice(checkDeviceName, dbs.read)
		checkGetErrorList := checkGetResult.GetErrors()

		fmt.Println("\nFIRST CONTROL POINT")

		if checkGetResult.RecordNotFound() {
			device = &models.Device{
				Name:         checkDeviceName,
				ScreenWidth:  frame.request.Device.Screen.Width,
				ScreenHeight: frame.request.Device.Screen.Height,
				PPI:          frame.request.Device.Screen.PPI,
				PlatformID:   platform.ID,
				BrandID:      brand.ID}
		} else if len(checkGetErrorList) > 0 {
			return &models.Device{}, errorResponse(frame.context)
		}

		return checkGetResult.Value.(*models.Device), nil
	}

	if device != nil {
		createResult := models.CreateDevice(device, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 || createResult.Value == nil {
			return &models.Device{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.Device), nil
	} else if len(getErrorList) > 0 || getResult.Value == nil {
		return &models.Device{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.Device), nil
}

func attachDevice(rating *models.Rating, platform *models.Platform, dbs *databases, frame *frame) error {
	var brand *models.Brand
	var brandErr error

	if frame.request.Device.Brand != nil {
		brand, brandErr = getBrand(dbs, frame)

		if brandErr != nil {
			return brandErr
		}
	}

	device, deviceErr := getDevice(brand, platform, dbs, frame)

	if deviceErr == nil {
		rating.DeviceID = device.ID
	}

	return deviceErr
}

/*
*
* Brand
*
 */
func getBrand(dbs *databases, frame *frame) (*models.Brand, error) {
	getResult := models.GetBrand(*frame.request.Device.Brand, dbs.read)
	getErrorList := getResult.GetErrors()

	if getResult.RecordNotFound() {
		brand := &models.Brand{Name: *frame.request.Device.Brand}
		createResult := models.CreateBrand(brand, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 || createResult.Value == nil {
			return &models.Brand{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.Brand), nil
	} else if len(getErrorList) > 0 || getResult.Value == nil {
		return &models.Brand{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.Brand), nil
}

func validateRating(from int8, to int8, frame *frame) error {
	if (frame.request.Rating < from) || (frame.request.Rating > to) {
		errorMessage := fmt.Sprintf("Error validating rating: %v is not in range(%v, %v)",
			frame.request.Rating,
			from,
			to)

		return responses.ErrorResponse(http.StatusUnprocessableEntity, errorMessage, frame.context)
	}

	return nil
}

/*
*
* Message
*
 */
func hasMessage(request *parser.Request) bool {
	result := false

	if len(request.Comment) > 0 {
		result = true
	}

	return result
}
<commit_msg>Fixed bug<commit_after>package controller

import (
	"fmt"
	"net/http"

	"ratings/models"
	"ratings/parser"
	"ratings/responses"

	"github.com/jinzhu/gorm"
	"github.com/labstack/echo"
)

type (
	frame struct {
		request *parser.Request
		context echo.Context
	}

	databases struct {
		read  *gorm.DB
		write *gorm.DB
	}
)

func errorResponse(context echo.Context) error {
	return responses.ErrorResponse(http.StatusInternalServerError, "", context)
}

/*
*
* App
*
 */
func getApp(db *gorm.DB, frame *frame) (*models.App, error) {
	result := models.GetApp(frame.request.App.Key, db)

	errorList := result.GetErrors()

	if len(errorList) > 0 || result.Value == nil {
		return &models.App{}, errorResponse(frame.context)
	}

	return result.Value.(*models.App), nil
}

/*
*
* Platform
*
 */
func getPlatform(db *gorm.DB, frame *frame) (*models.Platform, error) {
	result := models.GetPlatform(frame.request.Platform.Key, db)
	errorList := result.GetErrors()

	if len(errorList) > 0 || result.Value == nil {
		return &models.Platform{}, errorResponse(frame.context)
	}

	return result.Value.(*models.Platform), nil
}

/*
*
* Range
*
 */
func getRange(db *gorm.DB, frame *frame) (*models.Range, error) {
	result := models.GetRange(frame.request.Range, db)
	errorList := result.GetErrors()

	if len(errorList) > 0 || result.Value == nil {
		return &models.Range{}, errorResponse(frame.context)
	}

	return result.Value.(*models.Range), nil
}

/*
*
* AppUser
*
 */
func hasAppUser(request *parser.Request) bool {
	if request.User == nil {
		return false
	}

	appuser := request.User
	nameLength := len(appuser.Name)
	emailLength := len(appuser.Email)
	mibaIDLength := len(appuser.MiBAID)

	if nameLength == 0 || (emailLength == 0 && mibaIDLength == 0) {
		return false
	}

	return true
}

func getAppUser(dbs *databases, frame *frame) (*models.AppUser, error) {
	var getResult *gorm.DB

	if hasMibaID := len(frame.request.User.MiBAID); hasMibaID > 0 {
		getResult = models.GetAppUser(frame.request.User.MiBAID, dbs.read)
	} else {
		getResult = models.GetAppUserByEmail(frame.request.User.Email, dbs.read)
	}

	getErrorList := getResult.GetErrors()

	if getResult.RecordNotFound() {
		appuser := &models.AppUser{
			Name:   frame.request.User.Name,
			Email:  frame.request.User.Email,
			MiBAID: frame.request.User.MiBAID}

		createResult := models.CreateAppUser(appuser, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 {
			return &models.AppUser{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.AppUser), nil
	} else if len(getErrorList) > 0 {
		return &models.AppUser{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.AppUser), nil
}

func attachAppUser(rating *models.Rating, dbs *databases, frame *frame) error {
	appUser, err := getAppUser(dbs, frame)

	if err == nil {
		rating.AppUserID = appUser.ID
	}

	return err
}

/*
*
* Browser
*
 */
func hasBrowser(request *parser.Request) bool {
	if request.Browser == nil {
		return false
	}

	return true
}

func getBrowser(dbs *databases, frame *frame) (*models.Browser, error) {
	getResult := models.GetBrowser(frame.request.Browser.Name, dbs.read)
	getErrorList := getResult.GetErrors()

	if getResult.RecordNotFound() {
		browser := &models.Browser{Name: frame.request.Browser.Name}
		createResult := models.CreateBrowser(browser, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 || createResult.Value == nil {
			return &models.Browser{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.Browser), nil
	} else if len(getErrorList) > 0 || getResult.Value == nil {
		return &models.Browser{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.Browser), nil
}

func attachBrowser(rating *models.Rating, dbs *databases, frame *frame) error {
	browser, err := getBrowser(dbs, frame)

	if err == nil {
		rating.BrowserID = browser.ID
		rating.BrowserVersion = frame.request.Browser.Version

		return err
	}

	return nil
}

/*
*
* Device
*
 */
func getDevice(brand *models.Brand, platform *models.Platform, dbs *databases, frame *frame) (*models.Device, error) {
	deviceName := frame.request.Device.Name
	screenWidth := frame.request.Device.Screen.Width
	screenHeight := frame.request.Device.Screen.Height

	if frame.request.Device.Name == "Desktop" {
		deviceName = fmt.Sprintf("Desktop %dx%d", screenWidth, screenHeight)
	}

	getResult := models.GetDevice(deviceName, dbs.read)
	getErrorList := getResult.GetErrors()

	var device *models.Device

	if getResult.RecordNotFound() {
		device = &models.Device{
			Name:         deviceName,
			ScreenWidth:  frame.request.Device.Screen.Width,
			ScreenHeight: frame.request.Device.Screen.Height,
			PPI:          frame.request.Device.Screen.PPI,
			PlatformID:   platform.ID}

		if brand != nil {
			device.BrandID = brand.ID
		}
	}

	if result, ok := getResult.Value.(*models.Device); (ok && brand != nil) && (result.BrandID != brand.ID) {
		checkDeviceName := fmt.Sprintf("%v (%v)", deviceName, brand.Name)
		checkGetResult := models.GetDevice(checkDeviceName, dbs.read)
		checkGetErrorList := checkGetResult.GetErrors()

		if checkGetResult.RecordNotFound() {
			device = &models.Device{
				Name:         checkDeviceName,
				ScreenWidth:  frame.request.Device.Screen.Width,
				ScreenHeight: frame.request.Device.Screen.Height,
				PPI:          frame.request.Device.Screen.PPI,
				PlatformID:   platform.ID,
				BrandID:      brand.ID}
		} else if len(checkGetErrorList) > 0 {
			return &models.Device{}, errorResponse(frame.context)
		} else {
			return checkGetResult.Value.(*models.Device), nil
		}
	}

	if device != nil {
		createResult := models.CreateDevice(device, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 || createResult.Value == nil {
			return &models.Device{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.Device), nil
	} else if len(getErrorList) > 0 || getResult.Value == nil {
		return &models.Device{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.Device), nil
}

func attachDevice(rating *models.Rating, platform *models.Platform, dbs *databases, frame *frame) error {
	var brand *models.Brand
	var brandErr error

	if frame.request.Device.Brand != nil {
		brand, brandErr = getBrand(dbs, frame)

		if brandErr != nil {
			return brandErr
		}
	}

	device, deviceErr := getDevice(brand, platform, dbs, frame)

	if deviceErr == nil {
		rating.DeviceID = device.ID
	}

	return deviceErr
}

/*
*
* Brand
*
 */
func getBrand(dbs *databases, frame *frame) (*models.Brand, error) {
	getResult := models.GetBrand(*frame.request.Device.Brand, dbs.read)
	getErrorList := getResult.GetErrors()

	if getResult.RecordNotFound() {
		brand := &models.Brand{Name: *frame.request.Device.Brand}
		createResult := models.CreateBrand(brand, dbs.write)
		createErrorList := createResult.GetErrors()

		if len(createErrorList) > 0 || createResult.Value == nil {
			return &models.Brand{}, errorResponse(frame.context)
		}

		return createResult.Value.(*models.Brand), nil
	} else if len(getErrorList) > 0 || getResult.Value == nil {
		return &models.Brand{}, errorResponse(frame.context)
	}

	return getResult.Value.(*models.Brand), nil
}

func validateRating(from int8, to int8, frame *frame) error {
	if (frame.request.Rating < from) || (frame.request.Rating > to) {
		errorMessage := fmt.Sprintf("Error validating rating: %v is not in range(%v, %v)",
			frame.request.Rating,
			from,
			to)

		return responses.ErrorResponse(http.StatusUnprocessableEntity, errorMessage, frame.context)
	}

	return nil
}

/*
*
* Message
*
 */
func hasMessage(request *parser.Request) bool {
	result := false

	if len(request.Comment) > 0 {
		result = true
	}

	return result
}
<|endoftext|>


<commit_before>/*
Copyright 2019 Gravitational, Inc.

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 auth

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"io/ioutil"

	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/events"
	"github.com/gravitational/teleport/lib/services"
	"github.com/gravitational/teleport/lib/utils"

	"github.com/beevik/etree"
	"github.com/gravitational/trace"
	saml2 "github.com/russellhaering/gosaml2"
)

func (s *AuthServer) UpsertSAMLConnector(connector services.SAMLConnector) error {
	return s.Identity.UpsertSAMLConnector(connector)
}

func (s *AuthServer) DeleteSAMLConnector(connectorName string) error {
	return s.Identity.DeleteSAMLConnector(connectorName)
}

func (s *AuthServer) CreateSAMLAuthRequest(req services.SAMLAuthRequest) (*services.SAMLAuthRequest, error) {
	connector, err := s.Identity.GetSAMLConnector(req.ConnectorID, true)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	provider, err := s.getSAMLProvider(connector)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	doc, err := provider.BuildAuthRequestDocument()
	if err != nil {
		return nil, trace.Wrap(err)
	}

	attr := doc.Root().SelectAttr("ID")
	if attr == nil || attr.Value == "" {
		return nil, trace.BadParameter("missing auth request ID")
	}

	req.ID = attr.Value
	req.RedirectURL, err = provider.BuildAuthURLFromDocument("", doc)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	err = s.Identity.CreateSAMLAuthRequest(req, defaults.SAMLAuthRequestTTL)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return &req, nil
}

func (s *AuthServer) getSAMLProvider(conn services.SAMLConnector) (*saml2.SAMLServiceProvider, error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	providerPack, ok := s.samlProviders[conn.GetName()]
	if ok && providerPack.connector.Equals(conn) {
		return providerPack.provider, nil
	}
	delete(s.samlProviders, conn.GetName())

	serviceProvider, err := conn.GetServiceProvider(s.clock)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	s.samlProviders[conn.GetName()] = &samlProvider{connector: conn, provider: serviceProvider}

	return serviceProvider, nil
}

// buildSAMLRoles takes a connector and claims and returns a slice of roles.
func (a *AuthServer) buildSAMLRoles(connector services.SAMLConnector, assertionInfo saml2.AssertionInfo) ([]string, error) {
	roles := connector.MapAttributes(assertionInfo)
	if len(roles) == 0 {
		return nil, trace.AccessDenied("unable to map attributes to role for connector: %v", connector.GetName())
	}

	return roles, nil
}

// assertionsToTraitMap extracts all string assertions and creates a map of traits
// that can be used to populate role variables.
func assertionsToTraitMap(assertionInfo saml2.AssertionInfo) map[string][]string {
	traits := make(map[string][]string)

	for _, assr := range assertionInfo.Values {
		var vals []string
		for _, value := range assr.Values {
			vals = append(vals, value.Value)
		}
		traits[assr.Name] = vals
	}

	return traits
}

func (a *AuthServer) calculateSAMLUser(connector services.SAMLConnector, assertionInfo saml2.AssertionInfo, request *services.SAMLAuthRequest) (*createUserParams, error) {
	var err error

	p := createUserParams{
		connectorName: connector.GetName(),
		username:      assertionInfo.NameID,
	}

	p.roles, err = a.buildSAMLRoles(connector, assertionInfo)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	p.traits = assertionsToTraitMap(assertionInfo)

	// Pick smaller for role: session TTL from role or requested TTL.
	roles, err := services.FetchRoles(p.roles, a.Access, p.traits)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	roleTTL := roles.AdjustSessionTTL(defaults.MaxCertDuration)
	p.sessionTTL = utils.MinTTL(roleTTL, request.CertTTL)

	return &p, nil
}

func (a *AuthServer) createSAMLUser(p *createUserParams) (services.User, error) {
	expires := a.GetClock().Now().UTC().Add(p.sessionTTL)

	log.Debugf("Generating dynamic SAML identity %v/%v with roles: %v.", p.connectorName, p.username, p.roles)

	user, err := services.GetUserMarshaler().GenerateUser(&services.UserV2{
		Kind:    services.KindUser,
		Version: services.V2,
		Metadata: services.Metadata{
			Name:      p.username,
			Namespace: defaults.Namespace,
			Expires:   &expires,
		},
		Spec: services.UserSpecV2{
			Roles:  p.roles,
			Traits: p.traits,
			SAMLIdentities: []services.ExternalIdentity{
				services.ExternalIdentity{
					ConnectorID: p.connectorName,
					Username:    p.username,
				},
			},
			CreatedBy: services.CreatedBy{
				User: services.UserRef{
					Name: "system",
				},
				Time: a.clock.Now().UTC(),
				Connector: &services.ConnectorRef{
					Type:     teleport.ConnectorSAML,
					ID:       p.connectorName,
					Identity: p.username,
				},
			},
		},
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// Get the user to check if it already exists or not.
	existingUser, err := a.Identity.GetUser(p.username, false)
	if err != nil {
		if !trace.IsNotFound(err) {
			return nil, trace.Wrap(err)
		}
	}

	// Overwrite exisiting user if it was created from an external identity provider.
	if existingUser != nil {
		connectorRef := existingUser.GetCreatedBy().Connector

		// If the exisiting user is a local user, fail and advise how to fix the problem.
		if connectorRef == nil {
			return nil, trace.AlreadyExists("local user with name '%v' already exists. Either change "+
				"NameID in assertion or remove local user and try again.", existingUser.GetName())
		}

		log.Debugf("Overwriting existing user '%v' created with %v connector %v.",
			existingUser.GetName(), connectorRef.Type, connectorRef.ID)
	}

	// Upsert the new user creating or updating whatever is in the database.
	err = a.UpsertUser(user)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return user, nil
}

func parseSAMLInResponseTo(response string) (string, error) {
	raw, _ := base64.StdEncoding.DecodeString(response)

	doc := etree.NewDocument()
	err := doc.ReadFromBytes(raw)
	if err != nil {
		// Attempt to inflate the response in case it happens to be compressed (as with one case at saml.oktadev.com)
		buf, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(raw)))
		if err != nil {
			return "", trace.Wrap(err)
		}

		doc = etree.NewDocument()
		err = doc.ReadFromBytes(buf)
		if err != nil {
			return "", trace.Wrap(err)
		}
	}

	if doc.Root() == nil {
		return "", trace.BadParameter("unable to parse response")
	}

	// teleport only supports sending party initiated flows (Teleport sends an
	// AuthnRequest to the IdP and gets a SAMLResponse from the IdP). identity
	// provider initiated flows (where Teleport gets an unsolicited SAMLResponse
	// from the IdP) are not supported.
	el := doc.Root()
	responseTo := el.SelectAttr("InResponseTo")
	if responseTo == nil {
		message := "teleport does not support initiating login from a SAML identity provider, login must be initiated from either the Teleport Web UI or CLI"
		log.Infof(message)
		return "", trace.NotImplemented(message)
	}
	if responseTo.Value == "" {
		return "", trace.BadParameter("InResponseTo can not be empty")
	}
	return responseTo.Value, nil
}

// SAMLAuthResponse is returned when auth server validated callback parameters
// returned from SAML identity provider
type SAMLAuthResponse struct {
	// Username is an authenticated teleport username
	Username string `json:"username"`
	// Identity contains validated SAML identity
	Identity services.ExternalIdentity `json:"identity"`
	// Web session will be generated by auth server if requested in SAMLAuthRequest
	Session services.WebSession `json:"session,omitempty"`
	// Cert will be generated by certificate authority
	Cert []byte `json:"cert,omitempty"`
	// TLSCert is a PEM encoded TLS certificate
	TLSCert []byte `json:"tls_cert,omitempty"`
	// Req is an original SAML auth request
	Req services.SAMLAuthRequest `json:"req"`
	// HostSigners is a list of signing host public keys
	// trusted by proxy, used in console login
	HostSigners []services.CertAuthority `json:"host_signers"`
}

// ValidateSAMLResponse consumes attribute statements from SAML identity provider
func (a *AuthServer) ValidateSAMLResponse(samlResponse string) (*SAMLAuthResponse, error) {
	re, err := a.validateSAMLResponse(samlResponse)
	if err != nil {
		fields := events.EventFields{
			events.LoginMethod:        events.LoginMethodSAML,
			events.AuthAttemptSuccess: false,
			events.AuthAttemptErr:     err.Error(),
		}
		if re != nil && re.attributeStatements != nil {
			fields[events.IdentityAttributes] = re.attributeStatements
		}
		a.EmitAuditEvent(events.UserSSOLoginFailure, fields)
		return nil, trace.Wrap(err)
	}
	fields := events.EventFields{
		events.EventUser:          re.auth.Username,
		events.AuthAttemptSuccess: true,
		events.LoginMethod:        events.LoginMethodSAML,
	}
	if re != nil && re.attributeStatements != nil {
		fields[events.IdentityAttributes] = re.attributeStatements
	}
	a.EmitAuditEvent(events.UserSSOLogin, fields)
	return &re.auth, nil
}

type samlAuthResponse struct {
	auth                SAMLAuthResponse
	attributeStatements map[string][]string
}

func (a *AuthServer) validateSAMLResponse(samlResponse string) (*samlAuthResponse, error) {
	requestID, err := parseSAMLInResponseTo(samlResponse)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	request, err := a.Identity.GetSAMLAuthRequest(requestID)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	connector, err := a.Identity.GetSAMLConnector(request.ConnectorID, true)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	provider, err := a.getSAMLProvider(connector)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	assertionInfo, err := provider.RetrieveAssertionInfo(samlResponse)
	if err != nil {
		log.Warnf("Failed to retrieve SAML AssertionInfo from response: %v.", err)
		return nil, trace.AccessDenied("bad SAML response")
	}

	if assertionInfo.WarningInfo.InvalidTime {
		log.Warnf("Invalid time in SAML AssertionInfo.")
		return nil, trace.AccessDenied("bad SAML response")
	}

	if assertionInfo.WarningInfo.NotInAudience {
		log.Warnf("No audience in SAML AssertionInfo.")
		return nil, trace.AccessDenied("bad SAML response")
	}

	log.Debugf("Obtained SAML assertions for %q.", assertionInfo.NameID)
	re := &samlAuthResponse{
		attributeStatements: make(map[string][]string),
	}
	for key, val := range assertionInfo.Values {
		var vals []string
		for _, vv := range val.Values {
			vals = append(vals, vv.Value)
		}
		log.Debugf("SAML assertion: %q: %q.", key, vals)
		re.attributeStatements[key] = vals
	}

	log.Debugf("SAML assertion warnings: %+v.", assertionInfo.WarningInfo)

	if len(connector.GetAttributesToRoles()) == 0 {
		return re, trace.BadParameter("no attributes to roles mapping, check connector documentation")
	}
	log.Debugf("Applying %v SAML attribute to roles mappings.", len(connector.GetAttributesToRoles()))

	// Calculate (figure out name, roles, traits, session TTL) of user and
	// create the user in the backend.
	params, err := a.calculateSAMLUser(connector, *assertionInfo, request)
	if err != nil {
		return re, trace.Wrap(err)
	}
	user, err := a.createSAMLUser(params)
	if err != nil {
		return re, trace.Wrap(err)
	}

	// Auth was successful, return session, certificate, etc. to caller.
	re.auth = SAMLAuthResponse{
		Req: *request,
		Identity: services.ExternalIdentity{
			ConnectorID: params.connectorName,
			Username:    params.username,
		},
		Username: user.GetName(),
	}

	// If the request is coming from a browser, create a web session.
	if request.CreateWebSession {
		session, err := a.createWebSession(user, params.sessionTTL)
		if err != nil {
			return re, trace.Wrap(err)
		}

		re.auth.Session = session
	}

	// If a public key was provided, sign it and return a certificate.
	if len(request.PublicKey) != 0 {
		sshCert, tlsCert, err := a.createSessionCert(user, params.sessionTTL, request.PublicKey, request.Compatibility)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		clusterName, err := a.GetClusterName()
		if err != nil {
			return nil, trace.Wrap(err)
		}
		re.auth.Cert = sshCert
		re.auth.TLSCert = tlsCert

		// Return the host CA for this cluster only.
		authority, err := a.GetCertAuthority(services.CertAuthID{
			Type:       services.HostCA,
			DomainName: clusterName.GetClusterName(),
		}, false)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		re.auth.HostSigners = append(re.auth.HostSigners, authority)
	}

	return re, nil
}
<commit_msg>Added clarification to error handling in audit log when Teleport receives empty claims or attribute statements.<commit_after>/*
Copyright 2019 Gravitational, Inc.

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 auth

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"io/ioutil"

	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/events"
	"github.com/gravitational/teleport/lib/services"
	"github.com/gravitational/teleport/lib/utils"

	"github.com/beevik/etree"
	"github.com/gravitational/trace"
	saml2 "github.com/russellhaering/gosaml2"
)

func (s *AuthServer) UpsertSAMLConnector(connector services.SAMLConnector) error {
	return s.Identity.UpsertSAMLConnector(connector)
}

func (s *AuthServer) DeleteSAMLConnector(connectorName string) error {
	return s.Identity.DeleteSAMLConnector(connectorName)
}

func (s *AuthServer) CreateSAMLAuthRequest(req services.SAMLAuthRequest) (*services.SAMLAuthRequest, error) {
	connector, err := s.Identity.GetSAMLConnector(req.ConnectorID, true)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	provider, err := s.getSAMLProvider(connector)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	doc, err := provider.BuildAuthRequestDocument()
	if err != nil {
		return nil, trace.Wrap(err)
	}

	attr := doc.Root().SelectAttr("ID")
	if attr == nil || attr.Value == "" {
		return nil, trace.BadParameter("missing auth request ID")
	}

	req.ID = attr.Value
	req.RedirectURL, err = provider.BuildAuthURLFromDocument("", doc)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	err = s.Identity.CreateSAMLAuthRequest(req, defaults.SAMLAuthRequestTTL)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return &req, nil
}

func (s *AuthServer) getSAMLProvider(conn services.SAMLConnector) (*saml2.SAMLServiceProvider, error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	providerPack, ok := s.samlProviders[conn.GetName()]
	if ok && providerPack.connector.Equals(conn) {
		return providerPack.provider, nil
	}
	delete(s.samlProviders, conn.GetName())

	serviceProvider, err := conn.GetServiceProvider(s.clock)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	s.samlProviders[conn.GetName()] = &samlProvider{connector: conn, provider: serviceProvider}

	return serviceProvider, nil
}

// buildSAMLRoles takes a connector and claims and returns a slice of roles.
func (a *AuthServer) buildSAMLRoles(connector services.SAMLConnector, assertionInfo saml2.AssertionInfo) ([]string, error) {
	roles := connector.MapAttributes(assertionInfo)
	if len(roles) == 0 {
		return nil, trace.AccessDenied("unable to map attributes to role for connector: %v", connector.GetName())
	}

	return roles, nil
}

// assertionsToTraitMap extracts all string assertions and creates a map of traits
// that can be used to populate role variables.
func assertionsToTraitMap(assertionInfo saml2.AssertionInfo) map[string][]string {
	traits := make(map[string][]string)

	for _, assr := range assertionInfo.Values {
		var vals []string
		for _, value := range assr.Values {
			vals = append(vals, value.Value)
		}
		traits[assr.Name] = vals
	}

	return traits
}

func (a *AuthServer) calculateSAMLUser(connector services.SAMLConnector, assertionInfo saml2.AssertionInfo, request *services.SAMLAuthRequest) (*createUserParams, error) {
	var err error

	p := createUserParams{
		connectorName: connector.GetName(),
		username:      assertionInfo.NameID,
	}

	p.roles, err = a.buildSAMLRoles(connector, assertionInfo)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	p.traits = assertionsToTraitMap(assertionInfo)

	// Pick smaller for role: session TTL from role or requested TTL.
	roles, err := services.FetchRoles(p.roles, a.Access, p.traits)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	roleTTL := roles.AdjustSessionTTL(defaults.MaxCertDuration)
	p.sessionTTL = utils.MinTTL(roleTTL, request.CertTTL)

	return &p, nil
}

func (a *AuthServer) createSAMLUser(p *createUserParams) (services.User, error) {
	expires := a.GetClock().Now().UTC().Add(p.sessionTTL)

	log.Debugf("Generating dynamic SAML identity %v/%v with roles: %v.", p.connectorName, p.username, p.roles)

	user, err := services.GetUserMarshaler().GenerateUser(&services.UserV2{
		Kind:    services.KindUser,
		Version: services.V2,
		Metadata: services.Metadata{
			Name:      p.username,
			Namespace: defaults.Namespace,
			Expires:   &expires,
		},
		Spec: services.UserSpecV2{
			Roles:  p.roles,
			Traits: p.traits,
			SAMLIdentities: []services.ExternalIdentity{
				services.ExternalIdentity{
					ConnectorID: p.connectorName,
					Username:    p.username,
				},
			},
			CreatedBy: services.CreatedBy{
				User: services.UserRef{
					Name: "system",
				},
				Time: a.clock.Now().UTC(),
				Connector: &services.ConnectorRef{
					Type:     teleport.ConnectorSAML,
					ID:       p.connectorName,
					Identity: p.username,
				},
			},
		},
	})
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// Get the user to check if it already exists or not.
	existingUser, err := a.Identity.GetUser(p.username, false)
	if err != nil {
		if !trace.IsNotFound(err) {
			return nil, trace.Wrap(err)
		}
	}

	// Overwrite exisiting user if it was created from an external identity provider.
	if existingUser != nil {
		connectorRef := existingUser.GetCreatedBy().Connector

		// If the exisiting user is a local user, fail and advise how to fix the problem.
		if connectorRef == nil {
			return nil, trace.AlreadyExists("local user with name '%v' already exists. Either change "+
				"NameID in assertion or remove local user and try again.", existingUser.GetName())
		}

		log.Debugf("Overwriting existing user '%v' created with %v connector %v.",
			existingUser.GetName(), connectorRef.Type, connectorRef.ID)
	}

	// Upsert the new user creating or updating whatever is in the database.
	err = a.UpsertUser(user)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return user, nil
}

func parseSAMLInResponseTo(response string) (string, error) {
	raw, _ := base64.StdEncoding.DecodeString(response)

	doc := etree.NewDocument()
	err := doc.ReadFromBytes(raw)
	if err != nil {
		// Attempt to inflate the response in case it happens to be compressed (as with one case at saml.oktadev.com)
		buf, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(raw)))
		if err != nil {
			return "", trace.Wrap(err)
		}

		doc = etree.NewDocument()
		err = doc.ReadFromBytes(buf)
		if err != nil {
			return "", trace.Wrap(err)
		}
	}

	if doc.Root() == nil {
		return "", trace.BadParameter("unable to parse response")
	}

	// teleport only supports sending party initiated flows (Teleport sends an
	// AuthnRequest to the IdP and gets a SAMLResponse from the IdP). identity
	// provider initiated flows (where Teleport gets an unsolicited SAMLResponse
	// from the IdP) are not supported.
	el := doc.Root()
	responseTo := el.SelectAttr("InResponseTo")
	if responseTo == nil {
		message := "teleport does not support initiating login from a SAML identity provider, login must be initiated from either the Teleport Web UI or CLI"
		log.Infof(message)
		return "", trace.NotImplemented(message)
	}
	if responseTo.Value == "" {
		return "", trace.BadParameter("InResponseTo can not be empty")
	}
	return responseTo.Value, nil
}

// SAMLAuthResponse is returned when auth server validated callback parameters
// returned from SAML identity provider
type SAMLAuthResponse struct {
	// Username is an authenticated teleport username
	Username string `json:"username"`
	// Identity contains validated SAML identity
	Identity services.ExternalIdentity `json:"identity"`
	// Web session will be generated by auth server if requested in SAMLAuthRequest
	Session services.WebSession `json:"session,omitempty"`
	// Cert will be generated by certificate authority
	Cert []byte `json:"cert,omitempty"`
	// TLSCert is a PEM encoded TLS certificate
	TLSCert []byte `json:"tls_cert,omitempty"`
	// Req is an original SAML auth request
	Req services.SAMLAuthRequest `json:"req"`
	// HostSigners is a list of signing host public keys
	// trusted by proxy, used in console login
	HostSigners []services.CertAuthority `json:"host_signers"`
}

// ValidateSAMLResponse consumes attribute statements from SAML identity provider
func (a *AuthServer) ValidateSAMLResponse(samlResponse string) (*SAMLAuthResponse, error) {
	re, err := a.validateSAMLResponse(samlResponse)
	if err != nil {
		fields := events.EventFields{
			events.LoginMethod:        events.LoginMethodSAML,
			events.AuthAttemptSuccess: false,
			events.AuthAttemptErr:     err.Error(),
		}
		if re != nil && re.attributeStatements != nil {
			fields[events.IdentityAttributes] = re.attributeStatements
		}
		a.EmitAuditEvent(events.UserSSOLoginFailure, fields)
		return nil, trace.Wrap(err)
	}
	fields := events.EventFields{
		events.EventUser:          re.auth.Username,
		events.AuthAttemptSuccess: true,
		events.LoginMethod:        events.LoginMethodSAML,
	}
	if re != nil && re.attributeStatements != nil {
		fields[events.IdentityAttributes] = re.attributeStatements
	}
	a.EmitAuditEvent(events.UserSSOLogin, fields)
	return &re.auth, nil
}

type samlAuthResponse struct {
	auth                SAMLAuthResponse
	attributeStatements map[string][]string
}

func (a *AuthServer) validateSAMLResponse(samlResponse string) (*samlAuthResponse, error) {
	requestID, err := parseSAMLInResponseTo(samlResponse)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	request, err := a.Identity.GetSAMLAuthRequest(requestID)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	connector, err := a.Identity.GetSAMLConnector(request.ConnectorID, true)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	provider, err := a.getSAMLProvider(connector)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	assertionInfo, err := provider.RetrieveAssertionInfo(samlResponse)
	if err != nil {
		log.Warnf("Received response with incorrect or no claims/attribute statements. Please check the identity provider configuration to make sure that mappings for claims/attribute statements are set up correctly. <See: https://gravitational.com/teleport/docs/ssh_okta/>. Failed to retrieve SAML AssertionInfo from response: %v.", err)
		return nil, trace.AccessDenied("bad SAML response")
	}

	if assertionInfo.WarningInfo.InvalidTime {
		log.Warnf("Invalid time in SAML AssertionInfo.")
		return nil, trace.AccessDenied("bad SAML response")
	}

	if assertionInfo.WarningInfo.NotInAudience {
		log.Warnf("No audience in SAML AssertionInfo.")
		return nil, trace.AccessDenied("bad SAML response")
	}

	log.Debugf("Obtained SAML assertions for %q.", assertionInfo.NameID)
	re := &samlAuthResponse{
		attributeStatements: make(map[string][]string),
	}
	for key, val := range assertionInfo.Values {
		var vals []string
		for _, vv := range val.Values {
			vals = append(vals, vv.Value)
		}
		log.Debugf("SAML assertion: %q: %q.", key, vals)
		re.attributeStatements[key] = vals
	}

	log.Debugf("SAML assertion warnings: %+v.", assertionInfo.WarningInfo)

	if len(connector.GetAttributesToRoles()) == 0 {
		return re, trace.BadParameter("no attributes to roles mapping, check connector documentation")
	}
	log.Debugf("Applying %v SAML attribute to roles mappings.", len(connector.GetAttributesToRoles()))

	// Calculate (figure out name, roles, traits, session TTL) of user and
	// create the user in the backend.
	params, err := a.calculateSAMLUser(connector, *assertionInfo, request)
	if err != nil {
		return re, trace.Wrap(err)
	}
	user, err := a.createSAMLUser(params)
	if err != nil {
		return re, trace.Wrap(err)
	}

	// Auth was successful, return session, certificate, etc. to caller.
	re.auth = SAMLAuthResponse{
		Req: *request,
		Identity: services.ExternalIdentity{
			ConnectorID: params.connectorName,
			Username:    params.username,
		},
		Username: user.GetName(),
	}

	// If the request is coming from a browser, create a web session.
	if request.CreateWebSession {
		session, err := a.createWebSession(user, params.sessionTTL)
		if err != nil {
			return re, trace.Wrap(err)
		}

		re.auth.Session = session
	}

	// If a public key was provided, sign it and return a certificate.
	if len(request.PublicKey) != 0 {
		sshCert, tlsCert, err := a.createSessionCert(user, params.sessionTTL, request.PublicKey, request.Compatibility)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		clusterName, err := a.GetClusterName()
		if err != nil {
			return nil, trace.Wrap(err)
		}
		re.auth.Cert = sshCert
		re.auth.TLSCert = tlsCert

		// Return the host CA for this cluster only.
		authority, err := a.GetCertAuthority(services.CertAuthID{
			Type:       services.HostCA,
			DomainName: clusterName.GetClusterName(),
		}, false)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		re.auth.HostSigners = append(re.auth.HostSigners, authority)
	}

	return re, nil
}
<|endoftext|>


<commit_before>/**
 * go-mapbox Base Module
 * Provides a common base for API modules
 * See https://www.mapbox.com/api-documentation/ for API information
 *
 * https://github.com/ryankurte/go-mapbox
 * Copyright 2017 Ryan Kurte
 */

package base

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
)

const (
	// BaseURL Mapbox API base URL
	BaseURL = "https://api.mapbox.com"

	statusRateLimitExceeded = 429
)

// Base Mapbox API base
type Base struct {
	token string
	debug bool
}

// NewBase Create a new API base instance
func NewBase(token string) (*Base, error) {
	if token == "" {
		return nil, errors.New("Mapbox API token not found")
	}

	b := &Base{}

	b.token = token

	return b, nil
}

// SetDebug enables debug output for API calls
func (b *Base) SetDebug(debug bool) {
	b.debug = true
}

type MapboxApiMessage struct {
	Message string
}

// QueryRequest make a get with the provided query string and return the response if successful
func (b *Base) QueryRequest(query string, v *url.Values) (*http.Response, error) {
	// Add token to args
	v.Set("access_token", b.token)

	// Generate URL
	url := fmt.Sprintf("%s/%s", BaseURL, query)

	if b.debug {
		fmt.Printf("URL: %s\n", url)
	}

	// Create request object
	request, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	request.URL.RawQuery = v.Encode()

	// Create client instance
	client := &http.Client{}

	resp, err := client.Do(request)
	if err != nil {
		return nil, err
	}

	if b.debug {
		data, _ := httputil.DumpRequest(request, true)
		fmt.Printf("Request: %s", string(data))
		data, _ = httputil.DumpResponse(resp, false)
		fmt.Printf("Response: %s", string(data))
	}

	if resp.StatusCode == statusRateLimitExceeded {
		return nil, ErrorAPILimitExceeded
	}
	if resp.StatusCode == http.StatusUnauthorized {
		return nil, ErrorAPIUnauthorized
	}

	return resp, nil
}

// QueryBase Query the mapbox API and fill the provided instance with the returned JSON
// TODO: Rename this
func (b *Base) QueryBase(query string, v *url.Values, inst interface{}) error {
	// Make request
	resp, err := b.QueryRequest(query, v)
	if err != nil && resp.StatusCode != http.StatusBadRequest {
		return err
	}
	defer resp.Body.Close()

	// Read body into buffer
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	// Handle bad requests with messages
	if resp.StatusCode == http.StatusBadRequest {
		apiMessage := MapboxApiMessage{}
		messageErr := json.Unmarshal(body, &apiMessage)
		if messageErr == nil {
			return fmt.Errorf("api error: %s", apiMessage.Message)
		}
		return fmt.Errorf("Bad Request (400) - no message")
	}

	// Attempt to decode body into inst type
	err = json.Unmarshal(body, &inst)
	if err != nil {
		return err
	}

	return nil
}

// Query the mapbox API
// TODO: Depreciate this
func (b *Base) Query(api, version, mode, query string, v *url.Values, inst interface{}) error {

	// Generate URL
	queryString := fmt.Sprintf("%s/%s/%s/%s", api, version, mode, query)

	return b.QueryBase(queryString, v, inst)
}
<commit_msg>Update base.go<commit_after>/**
 * go-mapbox Base Module
 * Provides a common base for API modules
 * See https://www.mapbox.com/api-documentation/ for API information
 *
 * https://github.com/ryankurte/go-mapbox
 * Copyright 2017 Ryan Kurte
 */

package base

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
)

const (
	// BaseURL Mapbox API base URL
	BaseURL = "https://api.mapbox.com"

	statusRateLimitExceeded = 429
)

// Base Mapbox API base
type Base struct {
	token string
	debug bool
}

// NewBase Create a new API base instance
func NewBase(token string) (*Base, error) {
	if token == "" {
		return nil, errors.New("Mapbox API token not found")
	}

	b := &Base{}

	b.token = token

	return b, nil
}

// SetDebug enables debug output for API calls
func (b *Base) SetDebug(debug bool) {
	b.debug = true
}

type MapboxApiMessage struct {
	Message string
}

// QueryRequest make a get with the provided query string and return the response if successful
func (b *Base) QueryRequest(query string, v *url.Values) (*http.Response, error) {
	// Add token to args
	v.Set("access_token", b.token)

	// Generate URL
	url := fmt.Sprintf("%s/%s", BaseURL, query)

	if b.debug {
		fmt.Printf("URL: %s\n", url)
	}

	// Create request object
	request, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	request.URL.RawQuery = v.Encode()

	// Create client instance
	client := &http.Client{}

	resp, err := client.Do(request)
	if err != nil {
		return nil, err
	}

	if b.debug {
		data, _ := httputil.DumpRequest(request, true)
		fmt.Printf("Request: %s", string(data))
		data, _ = httputil.DumpResponse(resp, false)
		fmt.Printf("Response: %s", string(data))
	}

	if resp.StatusCode == statusRateLimitExceeded {
		return nil, ErrorAPILimitExceeded
	}
	if resp.StatusCode == http.StatusUnauthorized {
		return nil, ErrorAPIUnauthorized
	}

	return resp, nil
}

// QueryBase Query the mapbox API and fill the provided instance with the returned JSON
// TODO: Rename this
func (b *Base) QueryBase(query string, v *url.Values, inst interface{}) error {
	// Make request
	resp, err := b.QueryRequest(query, v)
	if err != nil && (resp == nil || resp.StatusCode != http.StatusBadRequest) {
		return err
	}
	defer resp.Body.Close()

	// Read body into buffer
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	// Handle bad requests with messages
	if resp.StatusCode == http.StatusBadRequest {
		apiMessage := MapboxApiMessage{}
		messageErr := json.Unmarshal(body, &apiMessage)
		if messageErr == nil {
			return fmt.Errorf("api error: %s", apiMessage.Message)
		}
		return fmt.Errorf("Bad Request (400) - no message")
	}

	// Attempt to decode body into inst type
	err = json.Unmarshal(body, &inst)
	if err != nil {
		return err
	}

	return nil
}

// Query the mapbox API
// TODO: Depreciate this
func (b *Base) Query(api, version, mode, query string, v *url.Values, inst interface{}) error {

	// Generate URL
	queryString := fmt.Sprintf("%s/%s/%s/%s", api, version, mode, query)

	return b.QueryBase(queryString, v, inst)
}
<|endoftext|>


<commit_before>// +build darwin freebsd openbsd

package isatty

import (
	"syscall"
	"unsafe"
)

const ioctlReadTermios = syscall.TIOCGETA

// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
	var termios syscall.Termios
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
	return err == 0
}
<commit_msg>Support NetBSD. Close #2<commit_after>// +build darwin freebsd openbsd netbsd

package isatty

import (
	"syscall"
	"unsafe"
)

const ioctlReadTermios = syscall.TIOCGETA

// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
	var termios syscall.Termios
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
	return err == 0
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// cleanup allows you to define a cleanup function that will be executed
// if your test is interrupted.

package test

import (
	"os"
	"os/signal"
)

// CleanupOnInterrupt will execute the function cleanup if an interrupt signal is caught
func CleanupOnInterrupt(cleanup func()) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for range c {
			cleanup()
			os.Exit(1)
		}
	}()
}

// TearDown will delete created names using clients.
func TearDown(clients *Clients, names ResourceNames) {
	if clients != nil && clients.ServingClient != nil {
		clients.ServingClient.Delete(
			[]string{names.Route},
			[]string{names.Config},
			[]string{names.Service},
		)
	}
}
<commit_msg>CleanupOnInterrupt() call multiple cleanup functions only once (#7417)<commit_after>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// cleanup allows you to define a cleanup function that will be executed
// if your test is interrupted.

package test

import (
	"os"
	"os/signal"
	"sync"
)

func init() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go cleanupOnInterrupt(c)
}

var cf struct {
	o sync.Once
	m sync.RWMutex
	f []func()
}

// cleanupOnInterrupt registers a signal handler and will execute a stack of functions if an interrupt signal is caught
func cleanupOnInterrupt(c chan os.Signal) {
	for range c {
		cf.o.Do(func() {
			cf.m.RLock()
			defer cf.m.RUnlock()
			for i := len(cf.f) - 1; i >= 0; i-- {
				cf.f[i]()
			}
			os.Exit(1)
		})
	}
}

// CleanupOnInterrupt stores cleanup functions to execute if an interrupt signal is caught
func CleanupOnInterrupt(cleanup func()) {
	cf.m.Lock()
	defer cf.m.Unlock()
	cf.f = append(cf.f, cleanup)
}

// TearDown will delete created names using clients.
func TearDown(clients *Clients, names ResourceNames) {
	if clients != nil && clients.ServingClient != nil {
		clients.ServingClient.Delete(
			[]string{names.Route},
			[]string{names.Config},
			[]string{names.Service},
		)
	}
}
<|endoftext|>