<commit_before>package cm16

import (
	"fmt"
	"net/http"

	"github.com/rightscale/rsc/rsapi"
)

// BuildRequest builds a HTTP request from a resource name and href and an action name and
// parameters.
// It is intended for generic clients that need to consume APIs in a generic maner.
// The method builds an HTTP request that can be fed to PerformRequest.
func (a *API) BuildRequest(resource, action, href string, params rsapi.APIParams) (*http.Request, error) {
	// First lookup metadata
	res, ok := GenMetadata[resource]
	if !ok {
		return nil, fmt.Errorf("No resource with name '%s'", resource)
	}
	act := res.GetAction(action)
	if act == nil {
		return nil, fmt.Errorf("No action with name '%s' on %s", action, resource)
	}

	// Now lookup action request HTTP method, url, params and payload.
	vars, err := res.ExtractVariables(href)
	if err != nil {
		return nil, err
	}
	actionURL, err := act.URL(vars)
	if err != nil {
		return nil, err
	}
	_, queryParams := rsapi.IdentifyParams(act, params)
	return a.BuildHTTPRequest("GET", actionURL.Path, "1.0", queryParams, nil)
}
<commit_msg>SS-2670 Fix the API version for cm16.<commit_after>package cm16

import (
	"fmt"
	"net/http"

	"github.com/rightscale/rsc/rsapi"
)

// BuildRequest builds a HTTP request from a resource name and href and an action name and
// parameters.
// It is intended for generic clients that need to consume APIs in a generic maner.
// The method builds an HTTP request that can be fed to PerformRequest.
func (a *API) BuildRequest(resource, action, href string, params rsapi.APIParams) (*http.Request, error) {
	// First lookup metadata
	res, ok := GenMetadata[resource]
	if !ok {
		return nil, fmt.Errorf("No resource with name '%s'", resource)
	}
	act := res.GetAction(action)
	if act == nil {
		return nil, fmt.Errorf("No action with name '%s' on %s", action, resource)
	}

	// Now lookup action request HTTP method, url, params and payload.
	vars, err := res.ExtractVariables(href)
	if err != nil {
		return nil, err
	}
	actionURL, err := act.URL(vars)
	if err != nil {
		return nil, err
	}
	_, queryParams := rsapi.IdentifyParams(act, params)
	return a.BuildHTTPRequest("GET", actionURL.Path, "1.6", queryParams, nil)
}
<|endoftext|>


<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"errors"
	"fmt"
	"github.com/kris-nova/kubicorn/cutil"
	"github.com/kris-nova/kubicorn/logger"
	"github.com/kris-nova/kubicorn/state"
	"github.com/kris-nova/kubicorn/state/fs"
	"github.com/spf13/cobra"
	"os"
)

// applyCmd represents the apply command
var applyCmd = &cobra.Command{
	Use:   "apply",
	Short: "Apply a cluster resource to a cloud",
	Long:  `Apply a cluster resource to a cloud`,
	Run: func(cmd *cobra.Command, args []string) {
		err := RunApply(ao)
		if err != nil {
			logger.Critical(err.Error())
			os.Exit(1)
		}

	},
}

type ApplyOptions struct {
	Options
}

var ao = &ApplyOptions{}

func init() {
	RootCmd.AddCommand(applyCmd)
	applyCmd.Flags().StringVarP(&ao.StateStore, "state-store", "s", strEnvDef("KUBICORN_STATE_STORE", "fs"), "The state store type to use for the cluster")
	applyCmd.Flags().StringVarP(&ao.StateStorePath, "state-store-path", "p", strEnvDef("KUBICORN_STATE_STORE_PATH", "./_state"), "The state store path to use")
	applyCmd.Flags().StringVarP(&ao.Name, "name", "n", strEnvDef("KUBICORN_NAME", ""), "An optional name to use. If empty, will generate a random name.")
}

func RunApply(options *ApplyOptions) error {

	// Ensure we have a name
	name := options.Name
	if name == "" {
		return errors.New("Empty name. Must specify the name of the cluster to delete.")
	}

	// Expand state store path
	options.StateStorePath = expandPath(options.StateStorePath)

	// Register state store
	var stateStore state.ClusterStorer
	switch options.StateStore {
	case "fs":
		logger.Info("Selected [fs] state store")
		stateStore = fs.NewFileSystemStore(&fs.FileSystemStoreOptions{
			BasePath:    options.StateStorePath,
			ClusterName: name,
		})
	}

	cluster, err := stateStore.GetCluster()
	if err != nil {
		return fmt.Errorf("Unable to get cluster [%s]: %v", name, err)
	}

	reconciler, err := cutil.GetReconciler(cluster)
	if err != nil {
		return fmt.Errorf("Unable to get reconciler: %v", err)
	}

	actualCluster, err := reconciler.GetActual(cluster)
	if err != nil {
		return fmt.Errorf("Unable to get actual cluster: %v", err)
	}

	expectedCluster, err := reconciler.GetExpected(cluster)
	if err != nil {
		return fmt.Errorf("Unable to get expected cluster: %v", err)
	}

	err = reconciler.Reconcile(actualCluster, expectedCluster)
	if err != nil {
		return fmt.Errorf("Unable to reconcile cluster: %v", err)
	}

	return nil
}
<commit_msg>Fixing apply logging and output<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"errors"
	"fmt"
	"github.com/kris-nova/kubicorn/cutil"
	"github.com/kris-nova/kubicorn/logger"
	"github.com/kris-nova/kubicorn/state"
	"github.com/kris-nova/kubicorn/state/fs"
	"github.com/spf13/cobra"
	"os"
)

// applyCmd represents the apply command
var applyCmd = &cobra.Command{
	Use:   "apply",
	Short: "Apply a cluster resource to a cloud",
	Long:  `Apply a cluster resource to a cloud`,
	Run: func(cmd *cobra.Command, args []string) {
		err := RunApply(ao)
		if err != nil {
			logger.Critical(err.Error())
			os.Exit(1)
		}

	},
}

type ApplyOptions struct {
	Options
}

var ao = &ApplyOptions{}

func init() {
	RootCmd.AddCommand(applyCmd)
	applyCmd.Flags().StringVarP(&ao.StateStore, "state-store", "s", strEnvDef("KUBICORN_STATE_STORE", "fs"), "The state store type to use for the cluster")
	applyCmd.Flags().StringVarP(&ao.StateStorePath, "state-store-path", "p", strEnvDef("KUBICORN_STATE_STORE_PATH", "./_state"), "The state store path to use")
	applyCmd.Flags().StringVarP(&ao.Name, "name", "n", strEnvDef("KUBICORN_NAME", ""), "An optional name to use. If empty, will generate a random name.")
}

func RunApply(options *ApplyOptions) error {

	// Ensure we have a name
	name := options.Name
	if name == "" {
		return errors.New("Empty name. Must specify the name of the cluster to apply.")
	}

	// Expand state store path
	options.StateStorePath = expandPath(options.StateStorePath)

	// Register state store
	var stateStore state.ClusterStorer
	switch options.StateStore {
	case "fs":
		logger.Info("Selected [fs] state store")
		stateStore = fs.NewFileSystemStore(&fs.FileSystemStoreOptions{
			BasePath:    options.StateStorePath,
			ClusterName: name,
		})
	}

	cluster, err := stateStore.GetCluster()
	if err != nil {
		return fmt.Errorf("Unable to get cluster [%s]: %v", name, err)
	}
	logger.Info("Loaded cluster: %s", cluster.Name)
	reconciler, err := cutil.GetReconciler(cluster)
	if err != nil {
		return fmt.Errorf("Unable to get reconciler: %v", err)
	}

	logger.Info("Loading actual")
	actualCluster, err := reconciler.GetActual(cluster)
	if err != nil {
		return fmt.Errorf("Unable to get actual cluster: %v", err)
	}

	logger.Info("Loading expected")
	expectedCluster, err := reconciler.GetExpected(cluster)
	if err != nil {
		return fmt.Errorf("Unable to get expected cluster: %v", err)
	}

	logger.Info("Reconciling")
	err = reconciler.Reconcile(actualCluster, expectedCluster)
	if err != nil {
		return fmt.Errorf("Unable to reconcile cluster: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/vanng822/accesslog"
	"github.com/vanng822/r2router"
	"github.com/vanng822/recovery"
	"github.com/vanng822/rproxy"
	"log"
	"net/http"
)

func main() {
	var (
		host    string
		port    int
		apiHost string
		apiPort int
	)

	flag.StringVar(&host, "h", "127.0.0.1", "Host to listen on")
	flag.IntVar(&port, "p", 80, "Port number to listen on")
	flag.StringVar(&apiHost, "ah", "127.0.0.1", "Host for server admin api")
	flag.IntVar(&apiPort, "ap", 8080, "Port for server admin api")
	flag.Parse()

	proxyServer := rproxy.NewProxy()

	logger := accesslog.New()
	rec := recovery.NewRecovery()

	seefor := r2router.NewSeeforRouter()
	seefor.Before(rec.Handler)
	seefor.Before(logger.Handler)

	seefor.Group("/_server", func(r *r2router.GroupRouter) {
		r.Post("/backend", func(w http.ResponseWriter, req *http.Request, _ r2router.Params) {
			err, severConfig := proxyServer.ParseServerConfig(req)
			if err != nil {
				http.Error(w, fmt.Sprintf("Invalid server config, error: %s", err.Error()), http.StatusBadRequest)
				return
			}
			err = proxyServer.Register(severConfig.ServerName, severConfig.TargetUrl)
			if err != nil {
				http.Error(w,
					fmt.Sprintf("It was problem when adding new server, serverName: '%s', targetUrl: '%s', error: '%s'",
						severConfig.ServerName, severConfig.TargetUrl, err.Error()),
					http.StatusInternalServerError)
				return
			}
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("OK"))

		})
		// delete backend node
		r.Delete("/backend", func(w http.ResponseWriter, req *http.Request, _ r2router.Params) {
			err, severConfig := proxyServer.ParseServerConfig(req)
			if err != nil {
				http.Error(w, fmt.Sprintf("Invalid server config, error: %s", err.Error()), http.StatusBadRequest)
				return
			}
			err = proxyServer.Unregister(severConfig.ServerName, severConfig.TargetUrl)
			if err != nil {
				http.Error(w,
					fmt.Sprintf("It was problem when removing server, serverName: '%s', targetUrl: '%s', error: '%s'",
						severConfig.ServerName, severConfig.TargetUrl, err.Error()),
					http.StatusInternalServerError)
				return
			}
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("OK"))

		})
		// delete server
		r.Delete("/", func(w http.ResponseWriter, req *http.Request, _ r2router.Params) {
			req.ParseForm()
			serverName := req.Form.Get("serverName")
			if serverName == "" {
				http.Error(w, fmt.Sprintf("serverName is required"), http.StatusBadRequest)
				return
			}
			err := proxyServer.RemoveServer(serverName)
			if err != nil {
				http.Error(w,
					fmt.Sprintf("It was problem when removing server, serverName: '%s', error: '%s'",
						serverName, err.Error()),
					http.StatusInternalServerError)
				return
			}
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("OK"))
		})
	})

	http.Handle("/", rec.Handler(logger.Handler(proxyServer)))
	go http.ListenAndServe(fmt.Sprintf("%s:%d", apiHost, apiPort), seefor)
	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), nil))
}
<commit_msg>Fix cmd according to new api<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/vanng822/rproxy"
	"github.com/vanng822/accesslog"
	"github.com/vanng822/recovery"
	"log"
	"net/http"
)

func main() {
	var (
		host    string
		port    int
		apiHost string
		apiPort int
	)

	flag.StringVar(&host, "h", "127.0.0.1", "Host to listen on")
	flag.IntVar(&port, "p", 80, "Port number to listen on")
	flag.StringVar(&apiHost, "ah", "127.0.0.1", "Host for server admin api")
	flag.IntVar(&apiPort, "ap", 8080, "Port for server admin api")
	flag.Parse()

	logger := accesslog.New()
	rec := recovery.NewRecovery()

	proxyServer := rproxy.NewProxy()

	api := proxyServer.AdminAPI()

	http.Handle("/", rec.Handler(logger.Handler(proxyServer)))
	go http.ListenAndServe(fmt.Sprintf("%s:%d", apiHost, apiPort), api)
	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), nil))
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/jomkz/canary/engine"
	"github.com/spf13/cobra"
)

var queryCmd = &cobra.Command{
	Use:   "query",
	Short: "Query the public and FQDN IP addresses",
	Long:  "This command will query the public IP address for this host as well as the IP address for the provided FQDN.",
	Args:  cobra.ExactArgs(1),
	RunE:  executeQuery,
}

func init() {
	usage := `Usage:
  canary query <FQDN>

Flags:
  -h, --help   help for check
`

	queryCmd.SetUsageTemplate(usage)
	rootCmd.AddCommand(queryCmd)
}

// executeQuery will run query the public facing IP address for the host and
// the IP address for the given FQDN.
func executeQuery(cmd *cobra.Command, args []string) error {
	pip, err := engine.PublicAddress()
	if err != nil {
		return err
	}

	fip, err := engine.FQDNAddress(args[0])
	if err != nil {
		return err
	}

	fmt.Println("Public IP:", pip)
	fmt.Println("  FQDN IP:", fip)
	return nil
}
<commit_msg>Clean up comment<commit_after>package cmd

import (
	"fmt"

	"github.com/jomkz/canary/engine"
	"github.com/spf13/cobra"
)

var queryCmd = &cobra.Command{
	Use:   "query",
	Short: "Query the public and FQDN IP addresses",
	Long:  "This command will query the public IP address for this host as well as the IP address for the provided FQDN.",
	Args:  cobra.ExactArgs(1),
	RunE:  executeQuery,
}

func init() {
	usage := `Usage:
  canary query <FQDN>

Flags:
  -h, --help   help for check
`

	queryCmd.SetUsageTemplate(usage)
	rootCmd.AddCommand(queryCmd)
}

// executeQuery will query the public facing IP address for the host and the IP
// address for the given FQDN.
func executeQuery(cmd *cobra.Command, args []string) error {
	pip, err := engine.PublicAddress()
	if err != nil {
		return err
	}

	fip, err := engine.FQDNAddress(args[0])
	if err != nil {
		return err
	}

	fmt.Println("Public IP:", pip)
	fmt.Println("  FQDN IP:", fip)
	return nil
}
<|endoftext|>


<commit_before>package moka

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

var _ = Describe("Moka", func() {
	var collaborator CollaboratorDouble
	var subject Subject

	var failHandlerCalled bool
	var failHandlerMessage string

	BeforeEach(func() {
		failHandlerCalled = false
		failHandlerMessage = ""
		RegisterDoublesFailHandler(func(message string, _ ...int) {
			failHandlerCalled = true
			failHandlerMessage = message
		})

		collaborator = NewCollaboratorDouble()
		subject = NewSubject(collaborator)
	})

	It("supports allowing a method call on a double", func() {
		AllowDouble(collaborator).To(ReceiveCallTo("Query").With("arg").AndReturn("result"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result := subject.DelegateQuery("arg")

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
		Expect(result).To(Equal("result"))
	})

	It("makes tests fail on unexpected interactions", func() {
		collaborator.Query("unexpected")

		Expect(failHandlerCalled).To(BeTrue())
		Expect(failHandlerMessage).To(Equal("Unexpected interaction: Query(\"unexpected\")"))
	})

	It("supports expecting a method call on a double", func() {
		ExpectDouble(collaborator).To(ReceiveCallTo("Command").With("arg").AndReturn("result", nil))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result, _ := subject.DelegateCommand("arg")

		Expect(result).To(Equal("result"))

		VerifyCalls(collaborator)

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
	})

	It("supports allowing a method call on a double without specifying any args", func() {
		AllowDouble(collaborator).To(ReceiveCallTo("Query").AndReturn("result"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result := subject.DelegateQuery("anything")

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
		Expect(result).To(Equal("result"))
	})

	It("supports allowing a method call on a double with variadic args", func() {
		AllowDouble(collaborator).To(ReceiveCallTo("VariadicQuery").With([]string{"arg1", "arg2", "arg3"}).AndReturn("result"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result := subject.DelegateVariadicQuery("arg1", "arg2", "arg3")

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
		Expect(result).To(Equal("result"))
	})
})

type Collaborator interface {
	Query(string) string
	Command(string) (string, error)
	VariadicQuery(...string) string
}

type CollaboratorDouble struct {
	Double
}

func NewCollaboratorDouble() CollaboratorDouble {
	return CollaboratorDouble{Double: NewStrictDoubleWithTypeOf(CollaboratorDouble{})}
}

func (d CollaboratorDouble) Query(arg string) string {
	returnValues, err := d.Call("Query", arg)
	if err != nil {
		return ""
	}

	return returnValues[0].(string)
}

func (d CollaboratorDouble) Command(arg string) (string, error) {
	returnValues, err := d.Call("Command", arg)
	if err != nil {
		return "", nil
	}

	returnedString, _ := returnValues[0].(string)
	returnedError, _ := returnValues[1].(error)

	return returnedString, returnedError
}

func (d CollaboratorDouble) VariadicQuery(args ...string) string {
	returnValues, err := d.Call("VariadicQuery", args)
	if err != nil {
		return ""
	}

	return returnValues[0].(string)
}

type Subject struct {
	collaborator Collaborator
}

func NewSubject(collaborator Collaborator) Subject {
	return Subject{collaborator: collaborator}
}

func (s Subject) DelegateQuery(arg string) string {
	return s.collaborator.Query(arg)
}

func (s Subject) DelegateCommand(arg string) (string, error) {
	return s.collaborator.Command(arg)
}

func (s Subject) DelegateVariadicQuery(args ...string) string {
	return s.collaborator.VariadicQuery(args...)
}
<commit_msg>Test expectation with no args or return values<commit_after>package moka

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

var _ = Describe("Moka", func() {
	var collaborator CollaboratorDouble
	var subject Subject

	var failHandlerCalled bool
	var failHandlerMessage string

	BeforeEach(func() {
		failHandlerCalled = false
		failHandlerMessage = ""
		RegisterDoublesFailHandler(func(message string, _ ...int) {
			failHandlerCalled = true
			failHandlerMessage = message
		})

		collaborator = NewCollaboratorDouble()
		subject = NewSubject(collaborator)
	})

	It("supports allowing a method call on a double", func() {
		AllowDouble(collaborator).To(ReceiveCallTo("Query").With("arg").AndReturn("result"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result := subject.DelegateQuery("arg")

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
		Expect(result).To(Equal("result"))
	})

	It("makes tests fail on unexpected interactions", func() {
		collaborator.Query("unexpected")

		Expect(failHandlerCalled).To(BeTrue())
		Expect(failHandlerMessage).To(Equal("Unexpected interaction: Query(\"unexpected\")"))
	})

	It("supports expecting a method call on a double", func() {
		ExpectDouble(collaborator).To(ReceiveCallTo("Command").With("arg").AndReturn("result", nil))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result, _ := subject.DelegateCommand("arg")

		Expect(result).To(Equal("result"))

		VerifyCalls(collaborator)

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
	})

	It("supports allowing a method call on a double without specifying any args", func() {
		AllowDouble(collaborator).To(ReceiveCallTo("Query").AndReturn("result"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result := subject.DelegateQuery("anything")

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
		Expect(result).To(Equal("result"))
	})

	It("supports expecting a method call on a double without specifying any args or return values", func() {
		ExpectDouble(collaborator).To(ReceiveCallTo("CommandWithNoReturnValues"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		subject.DelegateCommandWithNoReturnValues("arg")
		VerifyCalls(collaborator)

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
	})

	It("supports allowing a method call on a double with variadic args", func() {
		AllowDouble(collaborator).To(ReceiveCallTo("VariadicQuery").With([]string{"arg1", "arg2", "arg3"}).AndReturn("result"))

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)

		result := subject.DelegateVariadicQuery("arg1", "arg2", "arg3")

		Expect(failHandlerCalled).To(BeFalse(), failHandlerMessage)
		Expect(result).To(Equal("result"))
	})
})

type Collaborator interface {
	Query(string) string
	Command(string) (string, error)
	CommandWithNoReturnValues(string)
	VariadicQuery(...string) string
}

type CollaboratorDouble struct {
	Double
}

func NewCollaboratorDouble() CollaboratorDouble {
	return CollaboratorDouble{Double: NewStrictDoubleWithTypeOf(CollaboratorDouble{})}
}

func (d CollaboratorDouble) Query(arg string) string {
	returnValues, err := d.Call("Query", arg)
	if err != nil {
		return ""
	}

	return returnValues[0].(string)
}

func (d CollaboratorDouble) Command(arg string) (string, error) {
	returnValues, err := d.Call("Command", arg)
	if err != nil {
		return "", nil
	}

	returnedString, _ := returnValues[0].(string)
	returnedError, _ := returnValues[1].(error)

	return returnedString, returnedError
}

func (d CollaboratorDouble) CommandWithNoReturnValues(arg string) {
	d.Call("CommandWithNoReturnValues", arg)
}

func (d CollaboratorDouble) VariadicQuery(args ...string) string {
	returnValues, err := d.Call("VariadicQuery", args)
	if err != nil {
		return ""
	}

	return returnValues[0].(string)
}

type Subject struct {
	collaborator Collaborator
}

func NewSubject(collaborator Collaborator) Subject {
	return Subject{collaborator: collaborator}
}

func (s Subject) DelegateQuery(arg string) string {
	return s.collaborator.Query(arg)
}

func (s Subject) DelegateCommand(arg string) (string, error) {
	return s.collaborator.Command(arg)
}

func (s Subject) DelegateVariadicQuery(args ...string) string {
	return s.collaborator.VariadicQuery(args...)
}

func (s Subject) DelegateCommandWithNoReturnValues(arg string) {
	s.collaborator.CommandWithNoReturnValues(arg)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"path"

	"github.com/sbinet/go-commander"
	"github.com/sbinet/go-flag"
	"github.com/sbinet/go-github-client/client"
)

func git_make_cmd_dl_rm() *commander.Command {
	cmd := &commander.Command{
		Run:       git_run_cmd_dl_rm,
		UsageLine: "dl-rm [options] -repo=repo file-id",
		Short:     "deletes a download on github by id",
		Long: `
dl-rm deletes a download from a github repository by id.

ex:
 $ goctogit dl-rm -repo=mana-core 1
 $ goctogit dl-rm -repo=mana-core -org my-organization 1
`,
		Flag: *flag.NewFlagSet("git-dl-rm", flag.ExitOnError),
	}
	cmd.Flag.String("u", "", "github user account")
	cmd.Flag.String("repo", "", "name of the github repository")
	cmd.Flag.String("org", "", "github organization account")

	return cmd
}

func git_run_cmd_dl_rm(cmd *commander.Command, args []string) {
	n := "github-" + cmd.Name()
	if len(args) != 1 {
		err := fmt.Errorf("%s: needs a file-id to delete", n)
		handle_err(err)
	}
	file_id := args[0]

	repo_name := cmd.Flag.Lookup("repo").Value.Get().(string)
	if repo_name == "" {
		err := fmt.Errorf("%s: needs a github repository name to delete from", n)
		handle_err(err)
	}

	user := cmd.Flag.Lookup("u").Value.Get().(string)
	org := cmd.Flag.Lookup("org").Value.Get().(string)

	if user == "" {
		v, err := Cfg.String("go-octogit", "username")
		handle_err(err)
		user = v
	}

	password, err := Cfg.String("go-octogit", "password")
	handle_err(err)

	ghc, err := client.NewGithubClient(user, password, client.AUTH_USER_PASSWORD)
	handle_err(err)

	account := user
	// DELETE /repos/:owner/:repo/downloads/:id
	if org != "" {
		account = org
	}
	url := path.Join("repos", account, repo_name, "downloads", file_id)

	fmt.Printf("%s: deleting download id=%s from %s/%s...\n",
		n, file_id, account, repo_name)

	req, err := ghc.NewAPIRequest("DELETE", url, nil)
	handle_err(err)

	resp, err := ghc.RunRequest(req, new(http.Client))
	handle_err(err)

	sc := resp.RawHttpResponse.StatusCode
	switch sc {
	case 204:
		// all good
	case 404:
		err = fmt.Errorf("%s: no such file-id\n", n)
	default:
		err = fmt.Errorf("%s: request did not succeed. got (status=%d) %v\n", n, resp.RawHttpResponse.StatusCode, resp.RawHttpResponse)
	}
	handle_err(err)

	fmt.Printf("%s: deleting download id=%s from %s/%s... [done]\n",
		n, file_id, account, repo_name)
}

// EOF
<commit_msg>cosmetics<commit_after>package main

import (
	"fmt"
	"net/http"
	"path"

	"github.com/sbinet/go-commander"
	"github.com/sbinet/go-flag"
	"github.com/sbinet/go-github-client/client"
)

func git_make_cmd_dl_rm() *commander.Command {
	cmd := &commander.Command{
		Run:       git_run_cmd_dl_rm,
		UsageLine: "dl-rm [options] -repo=repo file-id",
		Short:     "deletes a download on github by id",
		Long: `
dl-rm deletes a download from a github repository by id.

ex:
 $ goctogit dl-rm -repo=mana-core 1
 $ goctogit dl-rm -repo=mana-core -org my-organization 1
`,
		Flag: *flag.NewFlagSet("git-dl-rm", flag.ExitOnError),
	}
	cmd.Flag.String("u", "", "github user account")
	cmd.Flag.String("repo", "", "name of the github repository")
	cmd.Flag.String("org", "", "github organization account")

	return cmd
}

func git_run_cmd_dl_rm(cmd *commander.Command, args []string) {
	n := "github-" + cmd.Name()
	if len(args) != 1 {
		err := fmt.Errorf("%s: needs a file-id to delete", n)
		handle_err(err)
	}
	file_id := args[0]

	repo_name := cmd.Flag.Lookup("repo").Value.Get().(string)
	if repo_name == "" {
		err := fmt.Errorf("%s: needs a github repository name to delete from", n)
		handle_err(err)
	}

	user := cmd.Flag.Lookup("u").Value.Get().(string)
	org := cmd.Flag.Lookup("org").Value.Get().(string)

	if user == "" {
		v, err := Cfg.String("go-octogit", "username")
		handle_err(err)
		user = v
	}

	password, err := Cfg.String("go-octogit", "password")
	handle_err(err)

	ghc, err := client.NewGithubClient(user, password, client.AUTH_USER_PASSWORD)
	handle_err(err)

	account := user
	// DELETE /repos/:owner/:repo/downloads/:id
	if org != "" {
		account = org
	}
	url := path.Join("repos", account, repo_name, "downloads", file_id)

	fmt.Printf("%s: deleting download id=%s from [%s/%s]...\n",
		n, file_id, account, repo_name)

	req, err := ghc.NewAPIRequest("DELETE", url, nil)
	handle_err(err)

	resp, err := ghc.RunRequest(req, new(http.Client))
	handle_err(err)

	sc := resp.RawHttpResponse.StatusCode
	switch sc {
	case 204:
		// all good
	case 404:
		err = fmt.Errorf("%s: no such file-id\n", n)
	default:
		err = fmt.Errorf("%s: request did not succeed. got (status=%d) %v\n", n, resp.RawHttpResponse.StatusCode, resp.RawHttpResponse)
	}
	handle_err(err)

	fmt.Printf("%s: deleting download id=%s from [%s/%s]... [done]\n",
		n, file_id, account, repo_name)
}

// EOF
<|endoftext|>


<commit_before>package multipass

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/httpserver"

	jose "gopkg.in/square/go-jose.v1"
)

// Portable errors
var (
	ErrInvalidToken = errors.New("invalid token")
)

// Multipass implements the http.Handler interface which can handle
// authentication and authorization of users and resources using signed JWT.
type Multipass struct {
	Resources []string
	Basepath  string
	SiteAddr  string
	Expires   time.Duration

	Handler HandleService
	signer  jose.Signer
	key     *rsa.PrivateKey
	tmpl    *template.Template
	mux     *http.ServeMux
}

// NewMultipass returns a new instance of Multipass with reasonalble defaults
// like a 2048 bit RSA key pair, /multipass as basepath, 24 hours before a
// token will expire.
func NewMultipass(basepath string) (*Multipass, error) {
	// Absolute the given basepath or set a default
	if len(basepath) > 0 {
		basepath = path.Join("/", basepath)
	} else {
		basepath = "/multipass"
	}

	// Generate the RSA key pari
	pk, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	signer, err := jose.NewSigner(jose.PS512, pk)
	if err != nil {
		return nil, err
	}

	// Load HTML templates
	tmpl, err := loadTemplates()
	if err != nil {
		return nil, err
	}

	m := &Multipass{
		Resources: []string{"/"},
		Basepath:  basepath,
		Expires:   time.Hour * 24,
		key:       pk,
		signer:    signer,
		tmpl:      tmpl,
	}

	// Create the router
	mux := http.NewServeMux()
	mux.HandleFunc(path.Join(basepath, "/"), m.rootHandler)
	mux.HandleFunc(path.Join(basepath, "/login"), m.loginHandler)
	mux.HandleFunc(path.Join(basepath, "/confirm"), m.confirmHandler)
	mux.HandleFunc(path.Join(basepath, "/signout"), m.signoutHandler)
	mux.HandleFunc(path.Join(basepath, "/pub.cer"), m.publickeyHandler)
	m.mux = mux

	return m, nil
}

// Claims are part of the JSON web token
type Claims struct {
	Handle    string   `json:"handle"`
	Resources []string `json:"resources"`
	Expires   int64    `json:"exp"`
}

// ServeHTTP satisfies the ServeHTTP interface
func (m *Multipass) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h, p := m.mux.Handler(r); len(p) > 0 {
		h.ServeHTTP(w, r)
		return
	}
	http.NotFound(w, r)
}

// rootHandler handles the "/" path of the Multipass handler.
// Shows login page when no JWT present
// Show continue or signout page when JWT is valid
// Show token invalid page when JWT is invalid
func (m *Multipass) rootHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {

		// Regular login page
		p := &page{
			Page:        loginPage,
			LoginPath:   path.Join(m.Basepath, "login"),
			SignoutPath: path.Join(m.Basepath, "signout"),
		}

		// Show login page when there is no token
		tokenStr, err := extractToken(r)
		if err != nil {
			if s := r.Referer(); !httpserver.Path(s).Matches(m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		var claims *Claims
		if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
			p.Page = tokenInvalidPage
			if s := r.Referer(); !httpserver.Path(s).Matches(m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		// Authorize handle claim
		if ok := m.Handler.Listed(claims.Handle); !ok {
			p.Page = tokenInvalidPage
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		if cookie, err := r.Cookie("next_url"); err == nil {
			p.NextURL = cookie.Value
		}
		p.Page = continueOrSignoutPage
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		if tokenStr := r.URL.Query().Get("token"); len(tokenStr) > 0 {
			cookie := &http.Cookie{
				Name:  "jwt_token",
				Value: tokenStr,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		if nexturl := r.URL.Query().Get("url"); len(nexturl) > 0 {
			cookie := &http.Cookie{
				Name:  "next_url",
				Value: nexturl,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	if r.Method == "POST" {
		r.ParseForm()
		handle := r.PostForm.Get("handle")
		if len(handle) > 0 {
			if m.Handler.Listed(handle) {
				token, err := m.AccessToken(handle)
				if err != nil {
					log.Print(err)
				}
				values := url.Values{}
				if s := r.PostForm.Get("url"); len(s) > 0 {
					values.Set("url", s)
				}
				loginURL, err := NewLoginURL(m.SiteAddr, m.Basepath, token, values)
				if err != nil {
					log.Print(err)
				}
				if err := m.Handler.Notify(handle, loginURL.String()); err != nil {
					log.Print(err)
				}
			}
			// Redirect even when the handle is not listed in order to prevent guessing
			location := path.Join(m.Basepath, "confirm")
			http.Redirect(w, r, location, http.StatusSeeOther)
			return
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) confirmHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")
		p := &page{
			Page: tokenSentPage,
		}
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// signoutHandler deletes the jwt_token cookie and redirect to the login
// location.
func (m *Multipass) signoutHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		if cookie, err := r.Cookie("jwt_token"); err == nil {
			cookie.Expires = time.Now().AddDate(-1, 0, 0)
			cookie.MaxAge = -1
			cookie.Path = "/"
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// publickeyHandler writes the public key to the given ResponseWriter to allow
// other to validate Multipass signed tokens.
func (m *Multipass) publickeyHandler(w http.ResponseWriter, r *http.Request) {
	data, err := x509.MarshalPKIXPublicKey(&m.key.PublicKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	block := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: data,
	}
	w.Header().Set("Content-Type", "application/pkix-cert")
	if err := pem.Encode(w, block); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

// AccessToken returns a new signed and serialized token with the given handle
// as a claim.
func (m *Multipass) AccessToken(handle string) (tokenStr string, err error) {
	exp := time.Now().Add(m.Expires)
	claims := &Claims{
		Handle:    handle,
		Resources: m.Resources,
		Expires:   exp.Unix(),
	}
	payload, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}
	jws, err := m.signer.Sign(payload)
	if err != nil {
		return "", err
	}

	return jws.CompactSerialize()
}

// NewLoginURL returns a login url which can be used as a time limited login.
// Optional values will be encoded in the login URL.
func NewLoginURL(siteaddr, basepath, token string, v url.Values) (*url.URL, error) {
	u, err := url.Parse(siteaddr)
	if err != nil {
		return u, err
	}
	u.Path = path.Join(basepath, "login")
	v.Set("token", token)
	u.RawQuery = v.Encode()
	return u, nil
}

func tokenHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	// Extract token from HTTP header, query parameter or cookie
	tokenStr, err := extractToken(r)
	if err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	var claims *Claims
	if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Authorize handle claim
	if ok := m.Handler.Listed(claims.Handle); !ok {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Verify path claim
	var match bool
	for _, path := range claims.Resources {
		if httpserver.Path(r.URL.Path).Matches(path) {
			match = true
			continue
		}
	}
	if !match {
		return http.StatusUnauthorized, ErrInvalidToken
	}

	// Pass on authorized handle to downstream handlers
	r.Header.Set("Multipass-Handle", claims.Handle)
	return http.StatusOK, nil
}

// extractToken returns the JWT token embedded in the given request.
// JWT tokens can be embedded in the header prefixed with "Bearer ", with a
// "token" key query parameter or a cookie named "jwt_token".
func extractToken(r *http.Request) (string, error) {
	//from header
	if h := r.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") {
		if len(h) > 7 {
			return h[7:], nil
		}
	}

	//from query parameter
	if token := r.URL.Query().Get("token"); len(token) > 0 {
		return token, nil
	}

	//from cookie
	if cookie, err := r.Cookie("jwt_token"); err == nil {
		return cookie.Value, nil
	}

	return "", fmt.Errorf("no token found")
}

func validateToken(token string, key rsa.PublicKey) (*Claims, error) {
	claims := &Claims{}

	// Verify token signature
	payload, err := verifyToken(token, key)
	if err != nil {
		return nil, err
	}
	// Unmarshal token claims
	if err := json.Unmarshal(payload, claims); err != nil {
		return nil, err
	}
	// Verify expire claim
	if time.Unix(claims.Expires, 0).Before(time.Now()) {
		return nil, errors.New("Token expired")
	}
	return claims, nil
}

func verifyToken(token string, key rsa.PublicKey) ([]byte, error) {
	var data []byte

	obj, err := jose.ParseSigned(token)
	if err != nil {
		return data, err
	}
	data, err = obj.Verify(&key)
	if err != nil {
		return data, err
	}
	return data, nil
}
<commit_msg>Replace httpserver httpserver.Mathes() with strings.HasPrefix to remove dependancy<commit_after>package multipass

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	jose "gopkg.in/square/go-jose.v1"
)

// Portable errors
var (
	ErrInvalidToken = errors.New("invalid token")
)

// Multipass implements the http.Handler interface which can handle
// authentication and authorization of users and resources using signed JWT.
type Multipass struct {
	Resources []string
	Basepath  string
	SiteAddr  string
	Expires   time.Duration

	Handler HandleService
	signer  jose.Signer
	key     *rsa.PrivateKey
	tmpl    *template.Template
	mux     *http.ServeMux
}

// NewMultipass returns a new instance of Multipass with reasonalble defaults
// like a 2048 bit RSA key pair, /multipass as basepath, 24 hours before a
// token will expire.
func NewMultipass(basepath string) (*Multipass, error) {
	// Absolute the given basepath or set a default
	if len(basepath) > 0 {
		basepath = path.Join("/", basepath)
	} else {
		basepath = "/multipass"
	}

	// Generate the RSA key pari
	pk, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	signer, err := jose.NewSigner(jose.PS512, pk)
	if err != nil {
		return nil, err
	}

	// Load HTML templates
	tmpl, err := loadTemplates()
	if err != nil {
		return nil, err
	}

	m := &Multipass{
		Resources: []string{"/"},
		Basepath:  basepath,
		Expires:   time.Hour * 24,
		key:       pk,
		signer:    signer,
		tmpl:      tmpl,
	}

	// Create the router
	mux := http.NewServeMux()
	mux.HandleFunc(path.Join(basepath, "/"), m.rootHandler)
	mux.HandleFunc(path.Join(basepath, "/login"), m.loginHandler)
	mux.HandleFunc(path.Join(basepath, "/confirm"), m.confirmHandler)
	mux.HandleFunc(path.Join(basepath, "/signout"), m.signoutHandler)
	mux.HandleFunc(path.Join(basepath, "/pub.cer"), m.publickeyHandler)
	m.mux = mux

	return m, nil
}

// Claims are part of the JSON web token
type Claims struct {
	Handle    string   `json:"handle"`
	Resources []string `json:"resources"`
	Expires   int64    `json:"exp"`
}

// ServeHTTP satisfies the ServeHTTP interface
func (m *Multipass) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h, p := m.mux.Handler(r); len(p) > 0 {
		h.ServeHTTP(w, r)
		return
	}
	http.NotFound(w, r)
}

// rootHandler handles the "/" path of the Multipass handler.
// Shows login page when no JWT present
// Show continue or signout page when JWT is valid
// Show token invalid page when JWT is invalid
func (m *Multipass) rootHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {

		// Regular login page
		p := &page{
			Page:        loginPage,
			LoginPath:   path.Join(m.Basepath, "login"),
			SignoutPath: path.Join(m.Basepath, "signout"),
		}

		// Show login page when there is no token
		tokenStr, err := extractToken(r)
		if err != nil {
			if s := r.Referer(); !strings.HasPrefix(s, m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		var claims *Claims
		if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
			p.Page = tokenInvalidPage
			if s := r.Referer(); !strings.HasPrefix(s, m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		// Authorize handle claim
		if ok := m.Handler.Listed(claims.Handle); !ok {
			p.Page = tokenInvalidPage
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		if cookie, err := r.Cookie("next_url"); err == nil {
			p.NextURL = cookie.Value
		}
		p.Page = continueOrSignoutPage
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		if tokenStr := r.URL.Query().Get("token"); len(tokenStr) > 0 {
			cookie := &http.Cookie{
				Name:  "jwt_token",
				Value: tokenStr,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		if nexturl := r.URL.Query().Get("url"); len(nexturl) > 0 {
			cookie := &http.Cookie{
				Name:  "next_url",
				Value: nexturl,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	if r.Method == "POST" {
		r.ParseForm()
		handle := r.PostForm.Get("handle")
		if len(handle) > 0 {
			if m.Handler.Listed(handle) {
				token, err := m.AccessToken(handle)
				if err != nil {
					log.Print(err)
				}
				values := url.Values{}
				if s := r.PostForm.Get("url"); len(s) > 0 {
					values.Set("url", s)
				}
				loginURL, err := NewLoginURL(m.SiteAddr, m.Basepath, token, values)
				if err != nil {
					log.Print(err)
				}
				if err := m.Handler.Notify(handle, loginURL.String()); err != nil {
					log.Print(err)
				}
			}
			// Redirect even when the handle is not listed in order to prevent guessing
			location := path.Join(m.Basepath, "confirm")
			http.Redirect(w, r, location, http.StatusSeeOther)
			return
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) confirmHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")
		p := &page{
			Page: tokenSentPage,
		}
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// signoutHandler deletes the jwt_token cookie and redirect to the login
// location.
func (m *Multipass) signoutHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		if cookie, err := r.Cookie("jwt_token"); err == nil {
			cookie.Expires = time.Now().AddDate(-1, 0, 0)
			cookie.MaxAge = -1
			cookie.Path = "/"
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// publickeyHandler writes the public key to the given ResponseWriter to allow
// other to validate Multipass signed tokens.
func (m *Multipass) publickeyHandler(w http.ResponseWriter, r *http.Request) {
	data, err := x509.MarshalPKIXPublicKey(&m.key.PublicKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	block := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: data,
	}
	w.Header().Set("Content-Type", "application/pkix-cert")
	if err := pem.Encode(w, block); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

// AccessToken returns a new signed and serialized token with the given handle
// as a claim.
func (m *Multipass) AccessToken(handle string) (tokenStr string, err error) {
	exp := time.Now().Add(m.Expires)
	claims := &Claims{
		Handle:    handle,
		Resources: m.Resources,
		Expires:   exp.Unix(),
	}
	payload, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}
	jws, err := m.signer.Sign(payload)
	if err != nil {
		return "", err
	}

	return jws.CompactSerialize()
}

// NewLoginURL returns a login url which can be used as a time limited login.
// Optional values will be encoded in the login URL.
func NewLoginURL(siteaddr, basepath, token string, v url.Values) (*url.URL, error) {
	u, err := url.Parse(siteaddr)
	if err != nil {
		return u, err
	}
	u.Path = path.Join(basepath, "login")
	v.Set("token", token)
	u.RawQuery = v.Encode()
	return u, nil
}

func tokenHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	// Extract token from HTTP header, query parameter or cookie
	tokenStr, err := extractToken(r)
	if err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	var claims *Claims
	if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Authorize handle claim
	if ok := m.Handler.Listed(claims.Handle); !ok {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Verify path claim
	var match bool
	for _, path := range claims.Resources {
		if strings.HasPrefix(r.URL.Path, path) {
			match = true
			continue
		}
	}
	if !match {
		return http.StatusUnauthorized, ErrInvalidToken
	}

	// Pass on authorized handle to downstream handlers
	r.Header.Set("Multipass-Handle", claims.Handle)
	return http.StatusOK, nil
}

// extractToken returns the JWT token embedded in the given request.
// JWT tokens can be embedded in the header prefixed with "Bearer ", with a
// "token" key query parameter or a cookie named "jwt_token".
func extractToken(r *http.Request) (string, error) {
	//from header
	if h := r.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") {
		if len(h) > 7 {
			return h[7:], nil
		}
	}

	//from query parameter
	if token := r.URL.Query().Get("token"); len(token) > 0 {
		return token, nil
	}

	//from cookie
	if cookie, err := r.Cookie("jwt_token"); err == nil {
		return cookie.Value, nil
	}

	return "", fmt.Errorf("no token found")
}

func validateToken(token string, key rsa.PublicKey) (*Claims, error) {
	claims := &Claims{}

	// Verify token signature
	payload, err := verifyToken(token, key)
	if err != nil {
		return nil, err
	}
	// Unmarshal token claims
	if err := json.Unmarshal(payload, claims); err != nil {
		return nil, err
	}
	// Verify expire claim
	if time.Unix(claims.Expires, 0).Before(time.Now()) {
		return nil, errors.New("Token expired")
	}
	return claims, nil
}

func verifyToken(token string, key rsa.PublicKey) ([]byte, error) {
	var data []byte

	obj, err := jose.ParseSigned(token)
	if err != nil {
		return data, err
	}
	data, err = obj.Verify(&key)
	if err != nil {
		return data, err
	}
	return data, nil
}
<|endoftext|>


<commit_before>package paralleldl

import (
	"net/http/httptest"
	"testing"

	"github.com/fortytw2/leaktest"
)

func setupTestDownload(t *testing.T, opt *Options) (*Client, *httptest.Server, func()) {
	dir, removeDir := createTestTempDir(t)
	opt.Output = dir
	client, _ := New(opt)

	ts := runTestServer()

	return client, ts, func() { removeDir() }
}

func TestDownload(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/ok1",
		ts.URL + "/ok2",
	}
	errCounts := client.Download(lists)
	if expected := int64(0); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error1(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxAttempts: 1,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/ok1",
		ts.URL + "/ok2",
		ts.URL + "/error",
	}
	errCounts := client.Download(lists)
	if expected := int64(1); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error2(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxErrorRequests: 1,
		MaxAttempts:      4,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/ok1",
		ts.URL + "/error",
		ts.URL + "/error",
	}
	errCounts := client.Download(lists)
	if expected := int64(2); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error3(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxConcurrents:   1,
		MaxErrorRequests: 1,
		MaxAttempts:      1024,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/error",
		ts.URL + "/error",
		ts.URL + "/error",
	}
	errCounts := client.Download(lists)
	if expected := int64(2); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error4(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxConcurrents:   1,
		MaxErrorRequests: 1,
		MaxAttempts:      1024,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/not-found",
		ts.URL + "/not-found",
	}
	errCounts := client.Download(lists)
	if expected := int64(2); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}
<commit_msg>fixed tests logic<commit_after>package paralleldl

import (
	"net/http/httptest"
	"testing"

	"github.com/fortytw2/leaktest"
)

func setupTestDownload(t *testing.T, opt *Options) (*Client, *httptest.Server, func()) {
	dir, removeDir := createTestTempDir(t)
	opt.Output = dir
	client, _ := New(opt)

	ts := runTestServer()

	return client, ts, func() { removeDir() }
}

func TestDownload(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/ok1",
		ts.URL + "/ok2",
	}
	errCounts := client.Download(lists)
	if expected := int64(0); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error1(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxAttempts: 1,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/ok1",
		ts.URL + "/ok2",
		ts.URL + "/error",
	}
	errCounts := client.Download(lists)
	if expected := int64(1); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error2(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxErrorRequests: 1,
		MaxAttempts:      2,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/ok1",
		ts.URL + "/error",
		ts.URL + "/error",
	}
	errCounts := client.Download(lists)
	if expected := int64(2); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error3(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxConcurrents:   1,
		MaxErrorRequests: 1,
		MaxAttempts:      1024,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/error",
		ts.URL + "/error",
		ts.URL + "/error",
	}
	errCounts := client.Download(lists)
	if expected := int64(2); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}

func TestDownload_Error4(t *testing.T) {
	defer leaktest.Check(t)()

	opt := &Options{
		MaxConcurrents:   1,
		MaxErrorRequests: 1,
		MaxAttempts:      1024,
	}
	client, ts, removeDir := setupTestDownload(t, opt)
	defer removeDir() // clean up
	defer ts.Close()

	lists := []string{
		ts.URL + "/not-found",
		ts.URL + "/not-found",
	}
	errCounts := client.Download(lists)
	if expected := int64(2); errCounts != expected {
		t.Errorf("expected %d, but got %d", expected, errCounts)
	}
}
<|endoftext|>


<commit_before>package request

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/client/metadata"
)

// A Request is the service request to be made.
type Request struct {
	Config     aws.Config
	ClientInfo metadata.ClientInfo
	Handlers   Handlers

	Retryer
	Time             time.Time
	ExpireTime       time.Duration
	Operation        *Operation
	HTTPRequest      *http.Request
	HTTPResponse     *http.Response
	Body             io.ReadSeeker
	BodyStart        int64 // offset from beginning of Body that the request body starts
	Params           interface{}
	Error            error
	Data             interface{}
	RequestID        string
	RetryCount       int
	Retryable        *bool
	RetryDelay       time.Duration
	NotHoist         bool
	SignedHeaderVals http.Header

	built bool
}

// An Operation is the service API operation to be made.
type Operation struct {
	Name       string
	HTTPMethod string
	HTTPPath   string
	*Paginator
}

// Paginator keeps track of pagination configuration for an API operation.
type Paginator struct {
	InputTokens     []string
	OutputTokens    []string
	LimitToken      string
	TruncationToken string
}

// New returns a new Request pointer for the service API
// operation and parameters.
//
// Params is any value of input parameters to be the request payload.
// Data is pointer value to an object which the request's response
// payload will be deserialized to.
func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
	retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {

	method := operation.HTTPMethod
	if method == "" {
		method = "POST"
	}
	p := operation.HTTPPath
	if p == "" {
		p = "/"
	}

	httpReq, _ := http.NewRequest(method, "", nil)

	var err error
	httpReq.URL, err = url.Parse(clientInfo.Endpoint + p)
	if err != nil {
		httpReq.URL = &url.URL{}
		err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
	}

	r := &Request{
		Config:     cfg,
		ClientInfo: clientInfo,
		Handlers:   handlers.Copy(),

		Retryer:     retryer,
		Time:        time.Now(),
		ExpireTime:  0,
		Operation:   operation,
		HTTPRequest: httpReq,
		Body:        nil,
		Params:      params,
		Error:       err,
		Data:        data,
	}
	r.SetBufferBody([]byte{})

	return r
}

// WillRetry returns if the request's can be retried.
func (r *Request) WillRetry() bool {
	return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
}

// ParamsFilled returns if the request's parameters have been populated
// and the parameters are valid. False is returned if no parameters are
// provided or invalid.
func (r *Request) ParamsFilled() bool {
	return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
}

// DataFilled returns true if the request's data for response deserialization
// target has been set and is a valid. False is returned if data is not
// set, or is invalid.
func (r *Request) DataFilled() bool {
	return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
}

// SetBufferBody will set the request's body bytes that will be sent to
// the service API.
func (r *Request) SetBufferBody(buf []byte) {
	r.SetReaderBody(bytes.NewReader(buf))
}

// SetStringBody sets the body of the request to be backed by a string.
func (r *Request) SetStringBody(s string) {
	r.SetReaderBody(strings.NewReader(s))
}

// SetReaderBody will set the request's body reader.
func (r *Request) SetReaderBody(reader io.ReadSeeker) {
	r.HTTPRequest.Body = newOffsetReader(reader, 0)
	r.Body = reader
}

// Presign returns the request's signed URL. Error will be returned
// if the signing fails.
func (r *Request) Presign(expireTime time.Duration) (string, error) {
	r.ExpireTime = expireTime
	r.NotHoist = false
	r.Sign()
	if r.Error != nil {
		return "", r.Error
	}
	return r.HTTPRequest.URL.String(), nil
}

// PresignRequest behaves just like presign, but hoists all headers and signs them.
// Also returns the signed hash back to the user
func (r *Request) PresignRequest(expireTime time.Duration) (string, http.Header, error) {
	r.ExpireTime = expireTime
	r.NotHoist = true
	r.Sign()
	if r.Error != nil {
		return "", nil, r.Error
	}
	return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
}

func debugLogReqError(r *Request, stage string, retrying bool, err error) {
	if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
		return
	}

	retryStr := "not retrying"
	if retrying {
		retryStr = "will retry"
	}

	r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
		stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
}

// Build will build the request's object so it can be signed and sent
// to the service. Build will also validate all the request's parameters.
// Anny additional build Handlers set on this request will be run
// in the order they were set.
//
// The request will only be built once. Multiple calls to build will have
// no effect.
//
// If any Validate or Build errors occur the build will stop and the error
// which occurred will be returned.
func (r *Request) Build() error {
	if !r.built {
		r.Handlers.Validate.Run(r)
		if r.Error != nil {
			debugLogReqError(r, "Validate Request", false, r.Error)
			return r.Error
		}
		r.Handlers.Build.Run(r)
		if r.Error != nil {
			debugLogReqError(r, "Build Request", false, r.Error)
			return r.Error
		}
		r.built = true
	}

	return r.Error
}

// Sign will sign the request retuning error if errors are encountered.
//
// Send will build the request prior to signing. All Sign Handlers will
// be executed in the order they were set.
func (r *Request) Sign() error {
	r.Build()
	if r.Error != nil {
		debugLogReqError(r, "Build Request", false, r.Error)
		return r.Error
	}

	r.Handlers.Sign.Run(r)
	return r.Error
}

// Send will send the request returning error if errors are encountered.
//
// Send will sign the request prior to sending. All Send Handlers will
// be executed in the order they were set.
//
// Canceling a request is non-deterministic. If a request has been canceled,
// then the transport will choose, randomly, one of the state channels during
// reads or getting the connection.
//
// readLoop() and getConn(req *Request, cm connectMethod)
// https://github.com/golang/go/blob/master/src/net/http/transport.go
func (r *Request) Send() error {
	for {
		if aws.BoolValue(r.Retryable) {
			if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
				r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
					r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
			}

			var body io.ReadCloser
			if reader, ok := r.HTTPRequest.Body.(*offsetReader); ok {
				body = reader.CloseAndCopy(r.BodyStart)
			} else {
				if r.Config.Logger != nil {
					r.Config.Logger.Log("Request body type has been overwritten. May cause race conditions")
				}
				r.Body.Seek(r.BodyStart, 0)
				body = ioutil.NopCloser(r.Body)
			}

			r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, body)
			if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
				// Closing response body. Since we are setting a new request to send off, this
				// response will get squashed and leaked.
				r.HTTPResponse.Body.Close()
			}
		}

		r.Sign()
		if r.Error != nil {
			return r.Error
		}

		r.Retryable = nil

		r.Handlers.Send.Run(r)
		if r.Error != nil {
			if strings.Contains(r.Error.Error(), "net/http: request canceled") {
				return r.Error
			}

			err := r.Error
			r.Handlers.Retry.Run(r)
			r.Handlers.AfterRetry.Run(r)
			if r.Error != nil {
				debugLogReqError(r, "Send Request", false, r.Error)
				return r.Error
			}
			debugLogReqError(r, "Send Request", true, err)
			continue
		}

		r.Handlers.UnmarshalMeta.Run(r)
		r.Handlers.ValidateResponse.Run(r)
		if r.Error != nil {
			err := r.Error
			r.Handlers.UnmarshalError.Run(r)
			r.Handlers.Retry.Run(r)
			r.Handlers.AfterRetry.Run(r)
			if r.Error != nil {
				debugLogReqError(r, "Validate Response", false, r.Error)
				return r.Error
			}
			debugLogReqError(r, "Validate Response", true, err)
			continue
		}

		r.Handlers.Unmarshal.Run(r)
		if r.Error != nil {
			err := r.Error
			r.Handlers.Retry.Run(r)
			r.Handlers.AfterRetry.Run(r)
			if r.Error != nil {
				debugLogReqError(r, "Unmarshal Response", false, r.Error)
				return r.Error
			}
			debugLogReqError(r, "Unmarshal Response", true, err)
			continue
		}

		break
	}

	return nil
}

// AddToUserAgent adds the string to the end of the request's current user agent.
func AddToUserAgent(r *Request, s string) {
	curUA := r.HTTPRequest.Header.Get("User-Agent")
	if len(curUA) > 0 {
		s = curUA + " " + s
	}
	r.HTTPRequest.Header.Set("User-Agent", s)
}
<commit_msg>Fixed typo in docstring (#708)<commit_after>package request

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/client/metadata"
)

// A Request is the service request to be made.
type Request struct {
	Config     aws.Config
	ClientInfo metadata.ClientInfo
	Handlers   Handlers

	Retryer
	Time             time.Time
	ExpireTime       time.Duration
	Operation        *Operation
	HTTPRequest      *http.Request
	HTTPResponse     *http.Response
	Body             io.ReadSeeker
	BodyStart        int64 // offset from beginning of Body that the request body starts
	Params           interface{}
	Error            error
	Data             interface{}
	RequestID        string
	RetryCount       int
	Retryable        *bool
	RetryDelay       time.Duration
	NotHoist         bool
	SignedHeaderVals http.Header

	built bool
}

// An Operation is the service API operation to be made.
type Operation struct {
	Name       string
	HTTPMethod string
	HTTPPath   string
	*Paginator
}

// Paginator keeps track of pagination configuration for an API operation.
type Paginator struct {
	InputTokens     []string
	OutputTokens    []string
	LimitToken      string
	TruncationToken string
}

// New returns a new Request pointer for the service API
// operation and parameters.
//
// Params is any value of input parameters to be the request payload.
// Data is pointer value to an object which the request's response
// payload will be deserialized to.
func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
	retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {

	method := operation.HTTPMethod
	if method == "" {
		method = "POST"
	}
	p := operation.HTTPPath
	if p == "" {
		p = "/"
	}

	httpReq, _ := http.NewRequest(method, "", nil)

	var err error
	httpReq.URL, err = url.Parse(clientInfo.Endpoint + p)
	if err != nil {
		httpReq.URL = &url.URL{}
		err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
	}

	r := &Request{
		Config:     cfg,
		ClientInfo: clientInfo,
		Handlers:   handlers.Copy(),

		Retryer:     retryer,
		Time:        time.Now(),
		ExpireTime:  0,
		Operation:   operation,
		HTTPRequest: httpReq,
		Body:        nil,
		Params:      params,
		Error:       err,
		Data:        data,
	}
	r.SetBufferBody([]byte{})

	return r
}

// WillRetry returns if the request's can be retried.
func (r *Request) WillRetry() bool {
	return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
}

// ParamsFilled returns if the request's parameters have been populated
// and the parameters are valid. False is returned if no parameters are
// provided or invalid.
func (r *Request) ParamsFilled() bool {
	return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
}

// DataFilled returns true if the request's data for response deserialization
// target has been set and is a valid. False is returned if data is not
// set, or is invalid.
func (r *Request) DataFilled() bool {
	return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
}

// SetBufferBody will set the request's body bytes that will be sent to
// the service API.
func (r *Request) SetBufferBody(buf []byte) {
	r.SetReaderBody(bytes.NewReader(buf))
}

// SetStringBody sets the body of the request to be backed by a string.
func (r *Request) SetStringBody(s string) {
	r.SetReaderBody(strings.NewReader(s))
}

// SetReaderBody will set the request's body reader.
func (r *Request) SetReaderBody(reader io.ReadSeeker) {
	r.HTTPRequest.Body = newOffsetReader(reader, 0)
	r.Body = reader
}

// Presign returns the request's signed URL. Error will be returned
// if the signing fails.
func (r *Request) Presign(expireTime time.Duration) (string, error) {
	r.ExpireTime = expireTime
	r.NotHoist = false
	r.Sign()
	if r.Error != nil {
		return "", r.Error
	}
	return r.HTTPRequest.URL.String(), nil
}

// PresignRequest behaves just like presign, but hoists all headers and signs them.
// Also returns the signed hash back to the user
func (r *Request) PresignRequest(expireTime time.Duration) (string, http.Header, error) {
	r.ExpireTime = expireTime
	r.NotHoist = true
	r.Sign()
	if r.Error != nil {
		return "", nil, r.Error
	}
	return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
}

func debugLogReqError(r *Request, stage string, retrying bool, err error) {
	if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
		return
	}

	retryStr := "not retrying"
	if retrying {
		retryStr = "will retry"
	}

	r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
		stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
}

// Build will build the request's object so it can be signed and sent
// to the service. Build will also validate all the request's parameters.
// Anny additional build Handlers set on this request will be run
// in the order they were set.
//
// The request will only be built once. Multiple calls to build will have
// no effect.
//
// If any Validate or Build errors occur the build will stop and the error
// which occurred will be returned.
func (r *Request) Build() error {
	if !r.built {
		r.Handlers.Validate.Run(r)
		if r.Error != nil {
			debugLogReqError(r, "Validate Request", false, r.Error)
			return r.Error
		}
		r.Handlers.Build.Run(r)
		if r.Error != nil {
			debugLogReqError(r, "Build Request", false, r.Error)
			return r.Error
		}
		r.built = true
	}

	return r.Error
}

// Sign will sign the request returning error if errors are encountered.
//
// Send will build the request prior to signing. All Sign Handlers will
// be executed in the order they were set.
func (r *Request) Sign() error {
	r.Build()
	if r.Error != nil {
		debugLogReqError(r, "Build Request", false, r.Error)
		return r.Error
	}

	r.Handlers.Sign.Run(r)
	return r.Error
}

// Send will send the request returning error if errors are encountered.
//
// Send will sign the request prior to sending. All Send Handlers will
// be executed in the order they were set.
//
// Canceling a request is non-deterministic. If a request has been canceled,
// then the transport will choose, randomly, one of the state channels during
// reads or getting the connection.
//
// readLoop() and getConn(req *Request, cm connectMethod)
// https://github.com/golang/go/blob/master/src/net/http/transport.go
func (r *Request) Send() error {
	for {
		if aws.BoolValue(r.Retryable) {
			if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
				r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
					r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
			}

			var body io.ReadCloser
			if reader, ok := r.HTTPRequest.Body.(*offsetReader); ok {
				body = reader.CloseAndCopy(r.BodyStart)
			} else {
				if r.Config.Logger != nil {
					r.Config.Logger.Log("Request body type has been overwritten. May cause race conditions")
				}
				r.Body.Seek(r.BodyStart, 0)
				body = ioutil.NopCloser(r.Body)
			}

			r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, body)
			if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
				// Closing response body. Since we are setting a new request to send off, this
				// response will get squashed and leaked.
				r.HTTPResponse.Body.Close()
			}
		}

		r.Sign()
		if r.Error != nil {
			return r.Error
		}

		r.Retryable = nil

		r.Handlers.Send.Run(r)
		if r.Error != nil {
			if strings.Contains(r.Error.Error(), "net/http: request canceled") {
				return r.Error
			}

			err := r.Error
			r.Handlers.Retry.Run(r)
			r.Handlers.AfterRetry.Run(r)
			if r.Error != nil {
				debugLogReqError(r, "Send Request", false, r.Error)
				return r.Error
			}
			debugLogReqError(r, "Send Request", true, err)
			continue
		}

		r.Handlers.UnmarshalMeta.Run(r)
		r.Handlers.ValidateResponse.Run(r)
		if r.Error != nil {
			err := r.Error
			r.Handlers.UnmarshalError.Run(r)
			r.Handlers.Retry.Run(r)
			r.Handlers.AfterRetry.Run(r)
			if r.Error != nil {
				debugLogReqError(r, "Validate Response", false, r.Error)
				return r.Error
			}
			debugLogReqError(r, "Validate Response", true, err)
			continue
		}

		r.Handlers.Unmarshal.Run(r)
		if r.Error != nil {
			err := r.Error
			r.Handlers.Retry.Run(r)
			r.Handlers.AfterRetry.Run(r)
			if r.Error != nil {
				debugLogReqError(r, "Unmarshal Response", false, r.Error)
				return r.Error
			}
			debugLogReqError(r, "Unmarshal Response", true, err)
			continue
		}

		break
	}

	return nil
}

// AddToUserAgent adds the string to the end of the request's current user agent.
func AddToUserAgent(r *Request, s string) {
	curUA := r.HTTPRequest.Header.Get("User-Agent")
	if len(curUA) > 0 {
		s = curUA + " " + s
	}
	r.HTTPRequest.Header.Set("User-Agent", s)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/robarchibald/configReader"
	"github.com/robarchibald/onedb"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"
)

type authConf struct {
	AuthServerListenPort                     int
	StoragePrefix                            string
	BackendType                              string
	BackendServer                            string
	BackendPort                              int
	BackendUser                              string
	BackendDatabase                          string
	BackendPassword                          string
	LdapBaseDn                               string
	LdapUserFilter                           string
	GetUserLoginQuery                        string
	GetSessionQuery                          string
	RenewSessionQuery                        string
	GetRememberMeQuery                       string
	RenewRememberMeQuery                     string
	AddUserQuery                             string
	VerifyEmailQuery                         string
	UpdateUserQuery                          string
	CreateLoginQuery                         string
	UpdateEmailAndInvalidateSessionsQuery    string
	UpdatePasswordAndInvalidateSessionsQuery string
	InvalidateUserSessionsQuery              string

	RedisServer         string
	RedisPort           int
	RedisPassword       string
	RedisMaxIdle        int
	RedisMaxConnections int
	ConcurrentDownloads int

	CookieBase64Key string

	SMTPServer              string
	SMTPPort                int
	SMTPFromEmail           string
	SMTPPassword            string
	EmailFromDisplayName    string
	VerifyEmailTemplate     string
	VerifyEmailSubject      string
	WelcomeTemplate         string
	WelcomeSubject          string
	NewLoginTemplate        string
	NewLoginSubject         string
	LockedOutTemplate       string
	LockedOutSubject        string
	EmailChangedTemplate    string
	EmailChangedSubject     string
	PasswordChangedTemplate string
	PasswordChangedSubject  string
}

type nginxauth struct {
	backend   Backender
	sb        SessionBackender
	mailer    Mailer
	cookieKey []byte
	conf      authConf
}

func main() {
	server, err := newNginxAuth()
	if err != nil {
		log.Fatal(err)
	}
	defer server.backend.Close()

	server.serve(server.conf.AuthServerListenPort)
}

func newNginxAuth() (*nginxauth, error) {
	config := authConf{}
	err := configReader.ReadFile("nginxauth.conf", &config)
	if err != nil {
		log.Fatal(err)
	}

	b := NewBackendMemory() //temporarily using the in-memory DB for testing
	/*l, err := NewLdapLoginStore(config.BackendServer, config.BackendPort, config.BackendUser, config.BackendPassword, config.LdapBaseDn)
	if err != nil {
		return nil, err
	}*/
	sb := NewRedisSessionBackend(config.RedisServer, config.RedisPort, config.RedisPassword, config.RedisMaxIdle, config.RedisMaxConnections, config.StoragePrefix)

	mailer, err := config.NewEmailer()
	if err != nil {
		return nil, err
	}

	cookieKey, err := decodeFromString(config.CookieBase64Key)
	if err != nil {
		return nil, err
	}

	return &nginxauth{b, sb, mailer, cookieKey, config}, nil
}

func (n *authConf) newOnedbBackend() (Backender, error) {
	db, err := onedb.NewPgx(n.BackendServer, uint16(n.BackendPort), n.BackendUser, n.BackendPassword, n.BackendDatabase)
	if err != nil {
		return nil, err
	}
	return &BackendOnedb{Db: db,
		GetUserLoginQuery:                        n.GetUserLoginQuery,
		GetSessionQuery:                          n.GetSessionQuery,
		RenewSessionQuery:                        n.RenewSessionQuery,
		GetRememberMeQuery:                       n.GetRememberMeQuery,
		RenewRememberMeQuery:                     n.RenewRememberMeQuery,
		AddUserQuery:                             n.AddUserQuery,
		VerifyEmailQuery:                         n.VerifyEmailQuery,
		UpdateUserQuery:                          n.UpdateUserQuery,
		CreateLoginQuery:                         n.CreateLoginQuery,
		UpdateEmailAndInvalidateSessionsQuery:    n.UpdateEmailAndInvalidateSessionsQuery,
		UpdatePasswordAndInvalidateSessionsQuery: n.UpdatePasswordAndInvalidateSessionsQuery,
		InvalidateUserSessionsQuery:              n.InvalidateUserSessionsQuery}, nil
}

func (n *authConf) NewEmailer() (*emailer, error) {
	sender := &smtpSender{n.SMTPServer, n.SMTPPort, n.SMTPFromEmail, n.SMTPPassword, n.EmailFromDisplayName}
	templateCache, err := template.ParseFiles(n.VerifyEmailTemplate, n.WelcomeTemplate,
		n.NewLoginTemplate, n.LockedOutTemplate, n.EmailChangedTemplate, n.PasswordChangedTemplate)
	if err != nil {
		return nil, err
	}
	return &emailer{
		templateCache:           templateCache,
		sender:                  sender,
		VerifyEmailTemplate:     n.VerifyEmailTemplate,
		VerifyEmailSubject:      n.VerifyEmailSubject,
		WelcomeTemplate:         n.WelcomeTemplate,
		WelcomeSubject:          n.WelcomeSubject,
		NewLoginTemplate:        n.NewLoginTemplate,
		NewLoginSubject:         n.NewLoginSubject,
		LockedOutTemplate:       n.LockedOutTemplate,
		LockedOutSubject:        n.LockedOutSubject,
		EmailChangedTemplate:    n.EmailChangedTemplate,
		EmailChangedSubject:     n.EmailChangedSubject,
		PasswordChangedTemplate: n.PasswordChangedTemplate,
		PasswordChangedSubject:  n.PasswordChangedSubject,
	}, nil
}

func (s *nginxauth) serve(port int) {
	http.HandleFunc("/auth", s.method("GET", auth))
	http.HandleFunc("/authBasic", s.method("GET", authBasic))
	http.HandleFunc("/createProfile", s.method("POST", createProfile))
	http.HandleFunc("/login", s.method("POST", login))
	http.HandleFunc("/register", s.method("POST", register))
	http.HandleFunc("/verifyEmail", s.method("POST", verifyEmail))
	http.HandleFunc("/updateEmail", s.method("POST", updateEmail))
	http.HandleFunc("/updatePassword", s.method("POST", updatePassword))

	http.ListenAndServe(fmt.Sprintf(":%d", port), fileLoggerHandler(handlers.CompressHandler(http.DefaultServeMux)))
}

func fileLoggerHandler(h http.Handler) http.Handler {
	logFile, err := os.OpenFile("nginxauth.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	return handlers.CombinedLoggingHandler(logFile, h)
}

func (s *nginxauth) method(name string, handler func(authStore AuthStorer, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != name {
			http.Error(w, "Unsupported method", http.StatusInternalServerError)
			return
		}
		secureOnly := strings.HasPrefix(r.Referer(), "https") // proxy to back-end so if referer is secure connection, we can use secureOnly cookies
		authStore := NewAuthStore(s.backend, s.sb, s.mailer, w, r, s.conf.StoragePrefix, s.cookieKey, secureOnly)
		handler(authStore, w, r)
	}
}
<commit_msg>remove reference to BackendOneDb<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/robarchibald/configReader"
	"github.com/robarchibald/onedb"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"
)

type authConf struct {
	AuthServerListenPort                     int
	StoragePrefix                            string
	BackendType                              string
	BackendServer                            string
	BackendPort                              int
	BackendUser                              string
	BackendDatabase                          string
	BackendPassword                          string
	LdapBaseDn                               string
	LdapUserFilter                           string
	GetUserLoginQuery                        string
	GetSessionQuery                          string
	RenewSessionQuery                        string
	GetRememberMeQuery                       string
	RenewRememberMeQuery                     string
	AddUserQuery                             string
	VerifyEmailQuery                         string
	UpdateUserQuery                          string
	CreateLoginQuery                         string
	UpdateEmailAndInvalidateSessionsQuery    string
	UpdatePasswordAndInvalidateSessionsQuery string
	InvalidateUserSessionsQuery              string

	RedisServer         string
	RedisPort           int
	RedisPassword       string
	RedisMaxIdle        int
	RedisMaxConnections int
	ConcurrentDownloads int

	CookieBase64Key string

	SMTPServer              string
	SMTPPort                int
	SMTPFromEmail           string
	SMTPPassword            string
	EmailFromDisplayName    string
	VerifyEmailTemplate     string
	VerifyEmailSubject      string
	WelcomeTemplate         string
	WelcomeSubject          string
	NewLoginTemplate        string
	NewLoginSubject         string
	LockedOutTemplate       string
	LockedOutSubject        string
	EmailChangedTemplate    string
	EmailChangedSubject     string
	PasswordChangedTemplate string
	PasswordChangedSubject  string
}

type nginxauth struct {
	backend   Backender
	sb        SessionBackender
	mailer    Mailer
	cookieKey []byte
	conf      authConf
}

func main() {
	server, err := newNginxAuth()
	if err != nil {
		log.Fatal(err)
	}
	defer server.backend.Close()

	server.serve(server.conf.AuthServerListenPort)
}

func newNginxAuth() (*nginxauth, error) {
	config := authConf{}
	err := configReader.ReadFile("nginxauth.conf", &config)
	if err != nil {
		log.Fatal(err)
	}

	b := NewBackendMemory() //temporarily using the in-memory DB for testing
	/*l, err := NewLdapLoginStore(config.BackendServer, config.BackendPort, config.BackendUser, config.BackendPassword, config.LdapBaseDn)
	if err != nil {
		return nil, err
	}*/
	sb := NewRedisSessionBackend(config.RedisServer, config.RedisPort, config.RedisPassword, config.RedisMaxIdle, config.RedisMaxConnections, config.StoragePrefix)

	mailer, err := config.NewEmailer()
	if err != nil {
		return nil, err
	}

	cookieKey, err := decodeFromString(config.CookieBase64Key)
	if err != nil {
		return nil, err
	}

	return &nginxauth{b, sb, mailer, cookieKey, config}, nil
}

func (n *authConf) NewEmailer() (*emailer, error) {
	sender := &smtpSender{n.SMTPServer, n.SMTPPort, n.SMTPFromEmail, n.SMTPPassword, n.EmailFromDisplayName}
	templateCache, err := template.ParseFiles(n.VerifyEmailTemplate, n.WelcomeTemplate,
		n.NewLoginTemplate, n.LockedOutTemplate, n.EmailChangedTemplate, n.PasswordChangedTemplate)
	if err != nil {
		return nil, err
	}
	return &emailer{
		templateCache:           templateCache,
		sender:                  sender,
		VerifyEmailTemplate:     n.VerifyEmailTemplate,
		VerifyEmailSubject:      n.VerifyEmailSubject,
		WelcomeTemplate:         n.WelcomeTemplate,
		WelcomeSubject:          n.WelcomeSubject,
		NewLoginTemplate:        n.NewLoginTemplate,
		NewLoginSubject:         n.NewLoginSubject,
		LockedOutTemplate:       n.LockedOutTemplate,
		LockedOutSubject:        n.LockedOutSubject,
		EmailChangedTemplate:    n.EmailChangedTemplate,
		EmailChangedSubject:     n.EmailChangedSubject,
		PasswordChangedTemplate: n.PasswordChangedTemplate,
		PasswordChangedSubject:  n.PasswordChangedSubject,
	}, nil
}

func (s *nginxauth) serve(port int) {
	http.HandleFunc("/auth", s.method("GET", auth))
	http.HandleFunc("/authBasic", s.method("GET", authBasic))
	http.HandleFunc("/createProfile", s.method("POST", createProfile))
	http.HandleFunc("/login", s.method("POST", login))
	http.HandleFunc("/register", s.method("POST", register))
	http.HandleFunc("/verifyEmail", s.method("POST", verifyEmail))
	http.HandleFunc("/updateEmail", s.method("POST", updateEmail))
	http.HandleFunc("/updatePassword", s.method("POST", updatePassword))

	http.ListenAndServe(fmt.Sprintf(":%d", port), fileLoggerHandler(handlers.CompressHandler(http.DefaultServeMux)))
}

func fileLoggerHandler(h http.Handler) http.Handler {
	logFile, err := os.OpenFile("nginxauth.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	return handlers.CombinedLoggingHandler(logFile, h)
}

func (s *nginxauth) method(name string, handler func(authStore AuthStorer, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != name {
			http.Error(w, "Unsupported method", http.StatusInternalServerError)
			return
		}
		secureOnly := strings.HasPrefix(r.Referer(), "https") // proxy to back-end so if referer is secure connection, we can use secureOnly cookies
		authStore := NewAuthStore(s.backend, s.sb, s.mailer, w, r, s.conf.StoragePrefix, s.cookieKey, secureOnly)
		handler(authStore, w, r)
	}
}
<|endoftext|>