<commit_before>package lispy

import (
	"fmt"
	"html"
)

func Common(li *Lispy) string {
	const htmlstr = `<%s{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonValue(li *Lispy) string {
	const htmlstr = `<%s{{if exist "value"}} value="{{getdel "value"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonName(li *Lispy) string {
	const htmlstr = `<%s{{if exist "name"}} name="{{getdel "name"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonCite(li *Lispy) string {
	const htmlstr = `<%s{{if exist "cite"}} cite="{{getdel "cite"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonTitle(li *Lispy) string {
	const htmlstr = `<%s{{if exist "title"}} title="{{getdel "title"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonSpan(li *Lispy) string {
	const htmlstr = `<%s{{if exist "span"}} span="{{getdel "span"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonDir(li *Lispy) string {
	const htmlstr = `<%s{{if exist "dir"}} dir="{{getdel "dir"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonCiteDateTime(li *Lispy) string {
	const htmlstr = `<%s{{if exist "cite"}} cite="{{getdel "cite"}}"{{end}}{{if exist "datetime"}} datetime="{{getdel "datetime"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonSrc(li *Lispy) string {
	const htmlstr = `<%s{{if exist "src"}} src="{{getdel "src"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func Blanks(li *Lispy) string {
	return ""
}

func JavaScript(li *Lispy) string {
	const htmlstr = `<script type="text/javascript">{{.Content|safe}}</script>`
	li.Content = html.UnescapeString(li.Content)
	return li.HtmlRender(htmlstr)
}

func CSS(li *Lispy) string {
	const htmlstr = `<style type="text/css">{{.Content|safe}}</style>`
	li.Content = html.UnescapeString(li.Content)
	return li.HtmlRender(htmlstr)
}

func Canvas(li *Lispy) string {
	const htmlstr = `<canvas{{if exist "id"}} id="{{getdel "id"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}></canvas>`

	if !li.Exist("id") {
		li.Set("id", li.Content)
	}

	return li.HtmlRender(htmlstr)
}

func ScriptFile(li *Lispy) string {
	const htmlstr = `<script src="{{.Content}}"></script>`
	return li.HtmlRender(htmlstr)
}
<commit_msg>Minor Fix!<commit_after>package lispy

import (
	"fmt"
	"html"
)

func Common(li *Lispy) string {
	const htmlstr = `<%s{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonValue(li *Lispy) string {
	const htmlstr = `<%s{{if exist "value"}} value="{{getdel "value"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonName(li *Lispy) string {
	const htmlstr = `<%s{{if exist "name"}} name="{{getdel "name"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonCite(li *Lispy) string {
	const htmlstr = `<%s{{if exist "cite"}} cite="{{getdel "cite"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonTitle(li *Lispy) string {
	const htmlstr = `<%s{{if exist "title"}} title="{{getdel "title"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonSpan(li *Lispy) string {
	const htmlstr = `<%s{{if exist "span"}} span="{{getdel "span"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonDir(li *Lispy) string {
	const htmlstr = `<%s{{if exist "dir"}} dir="{{getdel "dir"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonCiteDateTime(li *Lispy) string {
	const htmlstr = `<%s{{if exist "cite"}} cite="{{getdel "cite"}}"{{end}}{{if exist "datetime"}} datetime="{{getdel "datetime"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func CommonSrc(li *Lispy) string {
	const htmlstr = `<%s{{if exist "src"}} src="{{getdel "src"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}>{{.Content|parse}}</%s>`
	return li.HtmlRender(fmt.Sprintf(htmlstr, li.Name, li.Name))
}

func Blanks(li *Lispy) string {
	return ""
}

func JavaScript(li *Lispy) string {
	const htmlstr = `<script type="text/javascript">%s</script>`
	li.Content = html.UnescapeString(li.Content)
	return fmt.Sprintf(htmlstr, li.Content)
}

func CSS(li *Lispy) string {
	const htmlstr = `<style type="text/css" scoped>%s</style>`
	li.Content = html.UnescapeString(li.Content)
	return fmt.Sprintf(htmlstr, li.Content)
}

func Canvas(li *Lispy) string {
	const htmlstr = `<canvas{{if exist "id"}} id="{{getdel "id"}}"{{end}}{{range names}} {{.|attr}}="{{get .}}"{{end}}></canvas>`

	if !li.Exist("id") {
		li.Set("id", li.Content)
	}

	return li.HtmlRender(htmlstr)
}

func ScriptFile(li *Lispy) string {
	const htmlstr = `<script src="{{.Content}}"></script>`
	return li.HtmlRender(htmlstr)
}
<|endoftext|>


<commit_before>package pkc

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"fmt"
	"io/ioutil"
)

const KeySize = 3072

var defaultLabel = []byte{}

func MaxMessageLength(key *rsa.PublicKey) int {
	if key == nil {
		return 0
	}
	return (key.N.BitLen() / 8) - (2 * sha256.Size) - 2
}

func GenerateKey() (key *rsa.PrivateKey, err error) {
	return rsa.GenerateKey(rand.Reader, KeySize)
}

func Encrypt(pub *rsa.PublicKey, pt []byte) (ct []byte, err error) {
	if len(ct) > MaxMessageLength(pub) {
		err = fmt.Errorf("message is too long")
		return
	}

	hash := sha256.New()
	ct, err = rsa.EncryptOAEP(hash, rand.Reader, pub, pt, defaultLabel)
	return
}

func Decrypt(prv *rsa.PrivateKey, ct []byte) (pt []byte, err error) {
	hash := sha256.New()
	pt, err = rsa.DecryptOAEP(hash, rand.Reader, prv, ct, defaultLabel)
	return
}

func ExportPrivateKey(prv *rsa.PrivateKey, filename string) (err error) {
	cert := x509.MarshalPKCS1PrivateKey(prv)
	err = ioutil.WriteFile(filename, cert, 0600)
	return
}

func ExportPublicKey(pub *rsa.PublicKey, filename string) (err error) {
	cert, err := x509.MarshalPKIXPublicKey(pub)
	if err != nil {
		return
	}
	err = ioutil.WriteFile(filename, cert, 0644)
	return
}

func ImportPrivateKey(filename string) (prv *rsa.PrivateKey, err error) {
	cert, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	prv, err = x509.ParsePKCS1PrivateKey(cert)
	return
}

func ImportPublicKey(filename string) (pub *rsa.PublicKey, err error) {
	cert, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	in, err := x509.ParsePKIXPublicKey(cert)
	if err != nil {
		return nil, err
	}
	pub = in.(*rsa.PublicKey)
	return
}
<commit_msg>Add PEM import/export to RSA-PKC.<commit_after>package pkc

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
)

const KeySize = 3072

var defaultLabel = []byte{}

func MaxMessageLength(key *rsa.PublicKey) int {
	if key == nil {
		return 0
	}
	return (key.N.BitLen() / 8) - (2 * sha256.Size) - 2
}

func GenerateKey() (key *rsa.PrivateKey, err error) {
	return rsa.GenerateKey(rand.Reader, KeySize)
}

func Encrypt(pub *rsa.PublicKey, pt []byte) (ct []byte, err error) {
	if len(ct) > MaxMessageLength(pub) {
		err = fmt.Errorf("message is too long")
		return
	}

	hash := sha256.New()
	ct, err = rsa.EncryptOAEP(hash, rand.Reader, pub, pt, defaultLabel)
	return
}

func Decrypt(prv *rsa.PrivateKey, ct []byte) (pt []byte, err error) {
	hash := sha256.New()
	pt, err = rsa.DecryptOAEP(hash, rand.Reader, prv, ct, defaultLabel)
	return
}

func ExportPrivateKey(prv *rsa.PrivateKey, filename string) (err error) {
	cert := x509.MarshalPKCS1PrivateKey(prv)
	err = ioutil.WriteFile(filename, cert, 0600)
	return
}

func ExportPrivatePEM(prv *rsa.PrivateKey, filename string) (err error) {
	cert := x509.MarshalPKCS1PrivateKey(prv)
	blk := new(pem.Block)
	blk.Type = "RSA PRIVATE KEY"
	blk.Bytes = cert
	out, err := os.Create(filename)
	if err == nil {
		err = pem.Encode(out, blk)
	}
	return
}

func ExportPublicKey(pub *rsa.PublicKey, filename string) (err error) {
	cert, err := x509.MarshalPKIXPublicKey(pub)
	if err != nil {
		return
	}
	err = ioutil.WriteFile(filename, cert, 0644)
	return
}

func ExportPublicPEM(pub *rsa.PublicKey, filename string) (err error) {
	cert, err := x509.MarshalPKIXPublicKey(pub)
	if err != nil {
		return
	}
	blk := new(pem.Block)
	blk.Type = "RSA PUBLIC KEY"
	blk.Bytes = cert
	out, err := os.Create(filename)
	if err == nil {
		err = pem.Encode(out, blk)
	}
	return
}

func ImportPrivateKey(filename string) (prv *rsa.PrivateKey, err error) {
	cert, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	prv, err = x509.ParsePKCS1PrivateKey(cert)
	if err != nil {
		return
	}
	return
}

func ImportPublicKey(filename string) (pub *rsa.PublicKey, err error) {
	cert, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	in, err := x509.ParsePKIXPublicKey(cert)
	if err != nil {
		return nil, err
	}
	pub = in.(*rsa.PublicKey)
	return
}

func ImportPEM(filename string) (prv *rsa.PrivateKey, pub *rsa.PublicKey, err error) {
	cert, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	for {
		var blk *pem.Block
		blk, cert = pem.Decode(cert)
		if blk == nil {
			break
		}
		switch blk.Type {
		case "RSA PRIVATE KEY":
			prv, err = x509.ParsePKCS1PrivateKey(cert)
			if err != nil {
				return
			}
		case "RSA PUBLIC KEY":
			var in interface{}
			in, err = x509.ParsePKIXPublicKey(cert)
			if err != nil {
				return
			}
			pub = in.(*rsa.PublicKey)
		}
		if cert == nil {
			break
		} else if pub != nil && prv != nil {
			break
		}
	}
	return
}
<|endoftext|>


<commit_before>package sagas

import (
	"bytes"
	"fmt"
	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/gen"
	"github.com/leanovate/gopter/prop"
	"strings"
	"testing"
)

func TestsagaStateFactory(t *testing.T) {

	sagaId := "testSaga"
	job := []byte{0, 1, 2, 3, 4, 5}

	state, _ := makeSagaState("testSaga", job)
	if state.sagaId != sagaId {
		t.Error(fmt.Sprintf("SagaState SagaId should be the same as the SagaId passed to Factory Method"))
	}

	if !bytes.Equal(state.Job(), job) {
		t.Error(fmt.Sprintf("SagaState Job should be the same as the supplied Job passed to Factory Method"))
	}
}

func TestSagaState_ValidateSagaId(t *testing.T) {
	err := validateSagaId("")
	if err == nil {
		t.Error("Invalid Saga Id Should Return Error")
	}

	// validate the correct error is returned
	_, sErrorOk := err.(InvalidSagaMessageError)
	if !sErrorOk {
		t.Error("Expected Returned Error to be InvalidSagaMessageError")
	}
}

func TestSagaState_ValidateTaskId(t *testing.T) {
	err := validateTaskId("")
	if err == nil {
		t.Error(fmt.Sprintf("Invalid Task Id Should Return Error"))
	}

	// validate the correct error is returned
	_, sErrorOk := err.(InvalidSagaMessageError)
	if !sErrorOk {
		t.Error("Expected Returned Error to be InvalidSagaMessageError")
	}
}

func TestSagaState_Copy(t *testing.T) {
	s1, _ := makeSagaState("sagaId", nil)
	s2 := copySagaState(s1)

	if s1.SagaId() != s2.SagaId() {
		t.Error(fmt.Sprintf("Copy Should Preserve SagaId"))
	}
}

func TestSagaState_SagaStateNotMutatedDuringUpdate(t *testing.T) {
	s1, _ := makeSagaState("sagaId", nil)
	s2, _ := updateSagaState(s1, MakeStartTaskMessage("sagaId", "task1", []byte{1, 2, 3}))

	if s1.IsTaskStarted("task1") {
		t.Error(fmt.Sprintf("StartTaskMessage Should Not Mutate SagaState"))
	}

	if s1.GetStartTaskData("task1") != nil {
		t.Error(fmt.Sprintf("StartTaskMessage Should Not Mutate SagaState"))
	}

	updateSagaState(s2, MakeEndTaskMessage("sagaId", "task1", []byte{4, 5, 6}))

	if s2.IsTaskCompleted("task1") {
		t.Error(fmt.Sprintf("EndTaskMessage Should Not Mutate SagaState"))
	}

	if s2.GetEndTaskData("task1") != nil {
		t.Error(fmt.Sprintf("EndTaskMessage Should Not Mutate SagaState"))
	}
}

func Test_ValidateUpdateSagaState(t *testing.T) {
	parameters := gopter.DefaultTestParameters()
	parameters.MinSuccessfulTests = 1000
	properties := gopter.NewProperties(parameters)

	properties.Property("StartSaga message is never valid on an already started saga", prop.ForAll(
		func(state *SagaState, data []byte) bool {
			msg := MakeStartSagaMessage(state.SagaId(), data)
			newState, err := updateSagaState(state, msg)

			return err != nil && newState == nil
		},
		GenSagaState(),
		gen.SliceOf(gen.UInt8()),
	))

	// EndSaga messages are valid if a saga has not been Aborted and all StartTask have EndTask messages
	// If a saga has been aborted all StartTask messages must have corresponding StartCompTask / EndCompTask messages
	// for an EndSaga message to be valid.
	properties.Property("EndSaga message is Valid or returns an error", prop.ForAll(
		func(state *SagaState) bool {

			msg := MakeEndSagaMessage(state.SagaId())
			newState, err := updateSagaState(state, msg)

			validTransition := true
			for _, id := range state.GetTaskIds() {

				// if aborted all comp tasks must be completed for all tasks started
				// if not aborted all started tasks must be completed
				if state.IsSagaAborted() {
					if !(state.IsTaskStarted(id) &&
						state.IsCompTaskStarted(id) &&
						state.IsCompTaskCompleted(id)) {
						validTransition = false
					}
				} else {
					if !(state.IsTaskStarted(id) &&
						state.IsTaskCompleted(id)) {
						validTransition = false
					}
				}
			}

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsSagaCompleted()
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaState(),
	))

	// Abort messages are valid unless a Saga has been Completed
	properties.Property("AbortSaga message is valid or returns an error", prop.ForAll(
		func(state *SagaState) bool {
			validTransition := !state.IsSagaCompleted()

			msg := MakeAbortSagaMessage(state.SagaId())
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsSagaAborted()
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaState(),
	))

	// StartTask messages are valid unless a Saga has been Completed or Aborted
	properties.Property("StartTask message is valid or returns an Error", prop.ForAll(

		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := !state.IsSagaCompleted() && !state.IsSagaAborted() && !state.IsTaskCompleted(taskId)

			msg := MakeStartTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsTaskStarted(taskId) &&
				bytes.Equal(newState.GetStartTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	// EndTask messages are valid if there is a corresponding StartTask message and a Saga
	// has not been aborted or completed
	properties.Property("EndTask message is valid or returns an Error", prop.ForAll(
		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := !state.IsSagaCompleted() && !state.IsSagaAborted() &&
				state.IsTaskStarted(taskId)

			msg := MakeEndTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsTaskCompleted(taskId) &&
				bytes.Equal(newState.GetEndTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	properties.Property("StartCompTask message is valid or returns an Error", prop.ForAll(
		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := state.IsSagaAborted() && !state.IsSagaCompleted() &&
				state.IsTaskStarted(taskId) && !state.IsCompTaskCompleted(taskId)

			msg := MakeStartCompTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsCompTaskStarted(taskId) &&
				bytes.Equal(newState.GetStartCompTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	properties.Property("EndCompTask message is valid or returns an Error", prop.ForAll(
		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := state.IsSagaAborted() && !state.IsSagaCompleted() &&
				state.IsTaskStarted(taskId) && state.IsCompTaskStarted(taskId)

			msg := MakeEndCompTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsCompTaskCompleted(taskId) &&
				bytes.Equal(newState.GetEndCompTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	properties.Property("String method returns correct representation of SagaState", prop.ForAll(
		func(state *SagaState) bool {

			str := state.String()

			if !strings.Contains(str, fmt.Sprintf("SagaId: %v", state.SagaId())) {
				return false
			}

			if state.IsSagaAborted() && !strings.Contains(str, "SagaAborted: true") {
				return false
			}

			if !state.IsSagaAborted() && !strings.Contains(str, "SagaAborted: false") {
				return false
			}

			if state.IsSagaCompleted() && !strings.Contains(str, "SagaCompleted: true") {
				return false
			}

			if !state.IsSagaCompleted() && !strings.Contains(str, "SagaCompleted: false") {
				return false
			}

			if len(state.GetTaskIds()) > 0 {
				taskSplit := strings.Split(str, "Tasks: [")
				taskString := strings.Split(taskSplit[1], ",")

				for _, taskStr := range taskString {

					split := strings.Split(taskStr, ": ")

					if len(split) >= 2 {
						taskId := strings.TrimSpace(split[0])
						taskStates := strings.Split(split[1], "|")

						for _, taskState := range taskStates {

							ts := strings.TrimSpace(taskState)
							switch ts {
							case "Started":
								if !state.IsTaskStarted(taskId) {
									return false
								}
							case "Completed":
								if !state.IsTaskCompleted(taskId) {
									return false
								}
							case "CompTaskStarted":
								if !state.IsCompTaskStarted(taskId) {
									return false
								}
							case "CompTaskCompleted":
								if !state.IsCompTaskCompleted(taskId) {
									return false
								}
							default:
								fmt.Println(fmt.Sprintf("Unrecognized state taskId: %s, taskState: %s ", taskId, taskState))
								return false
							}
						}
					}
				}
			}

			return true
		},
		GenSagaState(),
	))

	properties.TestingRun(t)
}
<commit_msg>fix formatting<commit_after>package sagas

import (
	"bytes"
	"fmt"
	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/gen"
	"github.com/leanovate/gopter/prop"
	"strings"
	"testing"
)

func TestSagaStateFactory(t *testing.T) {

	sagaId := "testSaga"
	job := []byte{0, 1, 2, 3, 4, 5}

	state, _ := makeSagaState("testSaga", job)
	if state.sagaId != sagaId {
		t.Error(fmt.Sprintf("SagaState SagaId should be the same as the SagaId passed to Factory Method"))
	}

	if !bytes.Equal(state.Job(), job) {
		t.Error(fmt.Sprintf("SagaState Job should be the same as the supplied Job passed to Factory Method"))
	}
}

func TestSagaState_ValidateSagaId(t *testing.T) {
	err := validateSagaId("")
	if err == nil {
		t.Error("Invalid Saga Id Should Return Error")
	}

	// validate the correct error is returned
	_, sErrorOk := err.(InvalidSagaMessageError)
	if !sErrorOk {
		t.Error("Expected Returned Error to be InvalidSagaMessageError")
	}
}

func TestSagaState_ValidateTaskId(t *testing.T) {
	err := validateTaskId("")
	if err == nil {
		t.Error(fmt.Sprintf("Invalid Task Id Should Return Error"))
	}

	// validate the correct error is returned
	_, sErrorOk := err.(InvalidSagaMessageError)
	if !sErrorOk {
		t.Error("Expected Returned Error to be InvalidSagaMessageError")
	}
}

func TestSagaState_Copy(t *testing.T) {
	s1, _ := makeSagaState("sagaId", nil)
	s2 := copySagaState(s1)

	if s1.SagaId() != s2.SagaId() {
		t.Error(fmt.Sprintf("Copy Should Preserve SagaId"))
	}
}

func TestSagaState_SagaStateNotMutatedDuringUpdate(t *testing.T) {
	s1, _ := makeSagaState("sagaId", nil)
	s2, _ := updateSagaState(s1, MakeStartTaskMessage("sagaId", "task1", []byte{1, 2, 3}))

	if s1.IsTaskStarted("task1") {
		t.Error(fmt.Sprintf("StartTaskMessage Should Not Mutate SagaState"))
	}

	if s1.GetStartTaskData("task1") != nil {
		t.Error(fmt.Sprintf("StartTaskMessage Should Not Mutate SagaState"))
	}

	updateSagaState(s2, MakeEndTaskMessage("sagaId", "task1", []byte{4, 5, 6}))

	if s2.IsTaskCompleted("task1") {
		t.Error(fmt.Sprintf("EndTaskMessage Should Not Mutate SagaState"))
	}

	if s2.GetEndTaskData("task1") != nil {
		t.Error(fmt.Sprintf("EndTaskMessage Should Not Mutate SagaState"))
	}
}

func Test_ValidateUpdateSagaState(t *testing.T) {
	parameters := gopter.DefaultTestParameters()
	parameters.MinSuccessfulTests = 1000
	properties := gopter.NewProperties(parameters)

	properties.Property("StartSaga message is never valid on an already started saga", prop.ForAll(
		func(state *SagaState, data []byte) bool {
			msg := MakeStartSagaMessage(state.SagaId(), data)
			newState, err := updateSagaState(state, msg)

			return err != nil && newState == nil
		},
		GenSagaState(),
		gen.SliceOf(gen.UInt8()),
	))

	// EndSaga messages are valid if a saga has not been Aborted and all StartTask have EndTask messages
	// If a saga has been aborted all StartTask messages must have corresponding StartCompTask / EndCompTask messages
	// for an EndSaga message to be valid.
	properties.Property("EndSaga message is Valid or returns an error", prop.ForAll(
		func(state *SagaState) bool {

			msg := MakeEndSagaMessage(state.SagaId())
			newState, err := updateSagaState(state, msg)

			validTransition := true
			for _, id := range state.GetTaskIds() {

				// if aborted all comp tasks must be completed for all tasks started
				// if not aborted all started tasks must be completed
				if state.IsSagaAborted() {
					if !(state.IsTaskStarted(id) &&
						state.IsCompTaskStarted(id) &&
						state.IsCompTaskCompleted(id)) {
						validTransition = false
					}
				} else {
					if !(state.IsTaskStarted(id) &&
						state.IsTaskCompleted(id)) {
						validTransition = false
					}
				}
			}

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsSagaCompleted()
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaState(),
	))

	// Abort messages are valid unless a Saga has been Completed
	properties.Property("AbortSaga message is valid or returns an error", prop.ForAll(
		func(state *SagaState) bool {
			validTransition := !state.IsSagaCompleted()

			msg := MakeAbortSagaMessage(state.SagaId())
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsSagaAborted()
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaState(),
	))

	// StartTask messages are valid unless a Saga has been Completed or Aborted
	properties.Property("StartTask message is valid or returns an Error", prop.ForAll(

		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := !state.IsSagaCompleted() && !state.IsSagaAborted() && !state.IsTaskCompleted(taskId)

			msg := MakeStartTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsTaskStarted(taskId) &&
				bytes.Equal(newState.GetStartTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	// EndTask messages are valid if there is a corresponding StartTask message and a Saga
	// has not been aborted or completed
	properties.Property("EndTask message is valid or returns an Error", prop.ForAll(
		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := !state.IsSagaCompleted() && !state.IsSagaAborted() &&
				state.IsTaskStarted(taskId)

			msg := MakeEndTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsTaskCompleted(taskId) &&
				bytes.Equal(newState.GetEndTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	properties.Property("StartCompTask message is valid or returns an Error", prop.ForAll(
		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := state.IsSagaAborted() && !state.IsSagaCompleted() &&
				state.IsTaskStarted(taskId) && !state.IsCompTaskCompleted(taskId)

			msg := MakeStartCompTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsCompTaskStarted(taskId) &&
				bytes.Equal(newState.GetStartCompTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	properties.Property("EndCompTask message is valid or returns an Error", prop.ForAll(
		func(pair StateTaskPair, data []byte) bool {

			state := pair.state
			taskId := pair.taskId

			validTransition := state.IsSagaAborted() && !state.IsSagaCompleted() &&
				state.IsTaskStarted(taskId) && state.IsCompTaskStarted(taskId)

			msg := MakeEndCompTaskMessage(state.SagaId(), taskId, data)
			newState, err := updateSagaState(state, msg)

			// validate the correct error is returned
			_, sErrorOk := err.(InvalidSagaStateError)

			// either we made a valid transition and had a valid update or applying
			// this message is an invalidTransition and an error was returned.
			validUpdate := validTransition && err == nil &&
				newState != nil && newState.IsCompTaskCompleted(taskId) &&
				bytes.Equal(newState.GetEndCompTaskData(taskId), data)
			errorReturned := !validTransition && err != nil && sErrorOk && newState == nil

			return validUpdate || errorReturned
		},
		GenSagaStateAndTaskId(),
		gen.SliceOf(gen.UInt8()),
	))

	properties.Property("String method returns correct representation of SagaState", prop.ForAll(
		func(state *SagaState) bool {

			str := state.String()

			if !strings.Contains(str, fmt.Sprintf("SagaId: %v", state.SagaId())) {
				return false
			}

			if state.IsSagaAborted() && !strings.Contains(str, "SagaAborted: true") {
				return false
			}

			if !state.IsSagaAborted() && !strings.Contains(str, "SagaAborted: false") {
				return false
			}

			if state.IsSagaCompleted() && !strings.Contains(str, "SagaCompleted: true") {
				return false
			}

			if !state.IsSagaCompleted() && !strings.Contains(str, "SagaCompleted: false") {
				return false
			}

			if len(state.GetTaskIds()) > 0 {
				taskSplit := strings.Split(str, "Tasks: [")
				taskString := strings.Split(taskSplit[1], ",")

				for _, taskStr := range taskString {

					split := strings.Split(taskStr, ": ")

					if len(split) >= 2 {
						taskId := strings.TrimSpace(split[0])
						taskStates := strings.Split(split[1], "|")

						for _, taskState := range taskStates {

							ts := strings.TrimSpace(taskState)
							switch ts {
							case "Started":
								if !state.IsTaskStarted(taskId) {
									return false
								}
							case "Completed":
								if !state.IsTaskCompleted(taskId) {
									return false
								}
							case "CompTaskStarted":
								if !state.IsCompTaskStarted(taskId) {
									return false
								}
							case "CompTaskCompleted":
								if !state.IsCompTaskCompleted(taskId) {
									return false
								}
							default:
								fmt.Println(fmt.Sprintf("Unrecognized state taskId: %s, taskState: %s ", taskId, taskState))
								return false
							}
						}
					}
				}
			}

			return true
		},
		GenSagaState(),
	))

	properties.TestingRun(t)
}
<|endoftext|>


<commit_before>// circle.go

package circle

import (
	"bytes"
	"errors"
	"hash/adler32"

	"github.com/charlesetc/dive"
)

var hash func([]byte) uint32 = adler32.Checksum

type Circle struct {
	address []byte
	hash    uint32
	next    *Circle
}

func (c *Circle) String() string {
	var buffer bytes.Buffer
	for current, first := c, true; current.hash != 0 ||
		first; current, first = current.next, false {
		if !first {
			buffer.WriteString(" -> ")
		}
		buffer.Write(current.address)
	}
	return buffer.String()
}

const (
	ReplicationDepth int = 1
)

func NewCircleHead() *Circle {
	circle := new(Circle)
	circle.hash = 0 // 0 means it's the head
	// circle.address is undefined
	circle.next = circle
	return circle
}

func NewCircle(address []byte) *Circle {
	circle := new(Circle)
	circle.address = address
	circle.hash = hash(address)
	return circle
}

func NewCircleString(address string) *Circle {
	return NewCircle([]byte(address))
}

func (c *Circle) Add(incoming *Circle) *Circle {
	var current *Circle
	for current = c; current.next.hash < incoming.hash; current = current.next {
		if current.next.hash == 0 {
			break
		}
	}
	incoming.next = current.next
	current.next = incoming
	return incoming
}

func (c *Circle) AddString(address string) *Circle {
	return c.Add(NewCircleString(address))
}

func CircleFromList(strs []string) *Circle {
	circle := NewCircleHead()
	for _, str := range strs {
		circle.AddString(str)
	}
	return circle
}

func CircleFromNode(node *dive.Node) *Circle {
	circle := NewCircleHead()
	for _, rec := range dive.GetAliveFromMap(node.Members) {
		circle.AddString(rec.Address)
	}
	return circle
}

// Will loop forever with an empty node...
func (c *Circle) KeyAddress(key []byte) func() ([]byte, error) {
	hashed := hash(key)

	var current *Circle
	for current = c; current.next.hash < hashed; current = current.next {
		if current.next.hash == 0 {
			break
		}
	}

	i := 0
	return func() ([]byte, error) {
		output := current.address
		i++

		if i > ReplicationDepth {
			return []byte{}, errors.New("No more replications.")
		}

		current = current.next
		if current.hash == 0 {
			current = current.next
		}

		return output, nil
	}
}
<commit_msg>Fixed a bug with KeyAddress<commit_after>// circle.go

package circle

import (
	"bytes"
	"errors"
	"hash/adler32"

	"github.com/charlesetc/dive"
)

var hash func([]byte) uint32 = adler32.Checksum

type Circle struct {
	address []byte
	hash    uint32
	next    *Circle
}

func (c *Circle) String() string {
	var buffer bytes.Buffer
	for current, first := c, true; current.hash != 0 ||
		first; current, first = current.next, false {
		if !first {
			buffer.WriteString(" -> ")
		}
		buffer.Write(current.address)
	}
	return buffer.String()
}

const (
	ReplicationDepth int = 1
)

func NewCircleHead() *Circle {
	circle := new(Circle)
	circle.hash = 0 // 0 means it's the head
	// circle.address is undefined
	circle.next = circle
	return circle
}

func NewCircle(address []byte) *Circle {
	circle := new(Circle)
	circle.address = address
	circle.hash = hash(address)
	return circle
}

func NewCircleString(address string) *Circle {
	return NewCircle([]byte(address))
}

func (c *Circle) Add(incoming *Circle) *Circle {
	var current *Circle
	for current = c; current.next.hash < incoming.hash; current = current.next {
		if current.next.hash == 0 {
			break
		}
	}
	incoming.next = current.next
	current.next = incoming
	return incoming
}

func (c *Circle) AddString(address string) *Circle {
	return c.Add(NewCircleString(address))
}

func CircleFromList(strs []string) *Circle {
	circle := NewCircleHead()
	for _, str := range strs {
		circle.AddString(str)
	}
	return circle
}

func CircleFromNode(node *dive.Node) *Circle {
	circle := NewCircleHead()
	for _, rec := range dive.GetAliveFromMap(node.Members) {
		circle.AddString(rec.Address)
	}
	return circle
}

// Will loop forever with an empty node...
func (c *Circle) KeyAddress(key []byte) func() ([]byte, error) {
	hashed := hash(key)

	var current *Circle
	for current = c.next; current.next.hash == 0 ||
		current.next.hash < hashed; current = current.next {
	}

	i := 0
	return func() ([]byte, error) {
		output := current.address
		i++

		if i > ReplicationDepth {
			return []byte{}, errors.New("No more replications.")
		}

		current = current.next
		if current.hash == 0 {
			current = current.next
		}

		return output, nil
	}
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package transport

import (
	"fmt"
	"sync"

	"github.com/bradfitz/http2"
)

const (
	// The default value of flow control window size in HTTP2 spec.
	defaultWindowSize = 65535
	// The initial window size for flow control.
	initialWindowSize     = defaultWindowSize      // for an RPC
	initialConnWindowSize = defaultWindowSize * 16 // for a connection
)

// The following defines various control items which could flow through
// the control buffer of transport. They represent different aspects of
// control tasks, e.g., flow control, settings, streaming resetting, etc.
type windowUpdate struct {
	streamID  uint32
	increment uint32
}

func (windowUpdate) isItem() bool {
	return true
}

type settings struct {
	ack bool
	ss  []http2.Setting
}

func (settings) isItem() bool {
	return true
}

type resetStream struct {
	streamID uint32
	code     http2.ErrCode
}

func (resetStream) isItem() bool {
	return true
}

type flushIO struct {
}

func (flushIO) isItem() bool {
	return true
}

type ping struct {
	ack bool
}

func (ping) isItem() bool {
	return true
}

// quotaPool is a pool which accumulates the quota and sends it to acquire()
// when it is available.
type quotaPool struct {
	c chan int

	mu    sync.Mutex
	quota int
}

// newQuotaPool creates a quotaPool which has quota q available to consume.
func newQuotaPool(q int) *quotaPool {
	qb := &quotaPool{c: make(chan int, 1)}
	if q > 0 {
		qb.c <- q
	}
	return qb
}

// add adds n to the available quota and tries to send it on acquire.
func (qb *quotaPool) add(n int) {
	qb.mu.Lock()
	defer qb.mu.Unlock()
	qb.quota += n
	if qb.quota <= 0 {
		return
	}
	select {
	case qb.c <- qb.quota:
		qb.quota = 0
	default:
	}
}

// cancel cancels the pending quota sent on acquire, if any.
func (qb *quotaPool) cancel() {
	qb.mu.Lock()
	defer qb.mu.Unlock()
	select {
	case n := <-qb.c:
		qb.quota += n
	default:
	}
}

// reset cancels the pending quota sent on acquired, incremented by v and sends
// it back on acquire.
func (qb *quotaPool) reset(v int) {
	qb.mu.Lock()
	defer qb.mu.Unlock()
	select {
	case n := <-qb.c:
		qb.quota += n
	default:
	}
	qb.quota += v
	if qb.quota <= 0 {
		return
	}
	select {
	case qb.c <- qb.quota:
		qb.quota = 0
	default:
	}
}

// acquire returns the channel on which available quota amounts are sent.
func (qb *quotaPool) acquire() <-chan int {
	return qb.c
}

// inFlow deals with inbound flow control
type inFlow struct {
	// The inbound flow control limit for pending data.
	limit uint32
	// conn points to the shared connection-level inFlow that is shared
	// by all streams on that conn. It is nil for the inFlow on the conn
	// directly.
	conn *inFlow

	mu sync.Mutex
	// pendingData is the overall data which have been received but not been
	// consumed by applications.
	pendingData uint32
	// The amount of data the application has consumed but grpc has not sent
	// window update for them. Used to reduce window update frequency.
	pendingUpdate uint32
}

// onData is invoked when some data frame is received. It increments not only its
// own pendingData but also that of the associated connection-level flow.
func (f *inFlow) onData(n uint32) error {
	if n == 0 {
		return nil
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.pendingData+f.pendingUpdate+n > f.limit {
		return fmt.Errorf("recieved %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate+n, f.limit)
	}
	if f.conn != nil {
		if err := f.conn.onData(n); err != nil {
			return ConnectionErrorf("%v", err)
		}
	}
	f.pendingData += n
	return nil
}

// connOnRead updates the connection level states when the application consumes data.
func (f *inFlow) connOnRead(n uint32) uint32 {
	if n == 0 || f.conn != nil {
		return 0
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	f.pendingData -= n
	f.pendingUpdate += n
	if f.pendingUpdate >= f.limit/4 {
		ret := f.pendingUpdate
		f.pendingUpdate = 0
		return ret
	}
	return 0
}

// onRead is invoked when the application reads the data. It returns the window updates
// for both stream and connection level.
func (f *inFlow) onRead(n uint32) (swu, cwu uint32) {
	if n == 0 {
		return
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.pendingData == 0 {
		// pendingData has been adjusted by restoreConn.
		return
	}
	f.pendingData -= n
	f.pendingUpdate += n
	if f.pendingUpdate >= f.limit/4 {
		swu = f.pendingUpdate
		f.pendingUpdate = 0
	}
	cwu = f.conn.connOnRead(n)
	return
}

// restoreConn is invoked when a stream is terminated. It removes its stake in
// the connection-level flow and resets its own state.
func (f *inFlow) restoreConn() uint32 {
	if f.conn == nil {
		return 0
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	n := f.pendingData
	f.pendingData = 0
	f.pendingUpdate = 0
	return f.conn.connOnRead(n)
}
<commit_msg>small fix<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package transport

import (
	"fmt"
	"sync"

	"github.com/bradfitz/http2"
)

const (
	// The default value of flow control window size in HTTP2 spec.
	defaultWindowSize = 65535
	// The initial window size for flow control.
	initialWindowSize     = defaultWindowSize      // for an RPC
	initialConnWindowSize = defaultWindowSize * 16 // for a connection
)

// The following defines various control items which could flow through
// the control buffer of transport. They represent different aspects of
// control tasks, e.g., flow control, settings, streaming resetting, etc.
type windowUpdate struct {
	streamID  uint32
	increment uint32
}

func (windowUpdate) isItem() bool {
	return true
}

type settings struct {
	ack bool
	ss  []http2.Setting
}

func (settings) isItem() bool {
	return true
}

type resetStream struct {
	streamID uint32
	code     http2.ErrCode
}

func (resetStream) isItem() bool {
	return true
}

type flushIO struct {
}

func (flushIO) isItem() bool {
	return true
}

type ping struct {
	ack bool
}

func (ping) isItem() bool {
	return true
}

// quotaPool is a pool which accumulates the quota and sends it to acquire()
// when it is available.
type quotaPool struct {
	c chan int

	mu    sync.Mutex
	quota int
}

// newQuotaPool creates a quotaPool which has quota q available to consume.
func newQuotaPool(q int) *quotaPool {
	qb := &quotaPool{
		c: make(chan int, 1),
	}
	if q > 0 {
		qb.c <- q
	} else {
		qb.quota = q
	}
	return qb
}

// add adds n to the available quota and tries to send it on acquire.
func (qb *quotaPool) add(n int) {
	qb.mu.Lock()
	defer qb.mu.Unlock()
	qb.quota += n
	if qb.quota <= 0 {
		return
	}
	select {
	case qb.c <- qb.quota:
		qb.quota = 0
	default:
	}
}

// cancel cancels the pending quota sent on acquire, if any.
func (qb *quotaPool) cancel() {
	qb.mu.Lock()
	defer qb.mu.Unlock()
	select {
	case n := <-qb.c:
		qb.quota += n
	default:
	}
}

// reset cancels the pending quota sent on acquired, incremented by v and sends
// it back on acquire.
func (qb *quotaPool) reset(v int) {
	qb.mu.Lock()
	defer qb.mu.Unlock()
	select {
	case n := <-qb.c:
		qb.quota += n
	default:
	}
	qb.quota += v
	if qb.quota <= 0 {
		return
	}
	select {
	case qb.c <- qb.quota:
		qb.quota = 0
	default:
	}
}

// acquire returns the channel on which available quota amounts are sent.
func (qb *quotaPool) acquire() <-chan int {
	return qb.c
}

// inFlow deals with inbound flow control
type inFlow struct {
	// The inbound flow control limit for pending data.
	limit uint32
	// conn points to the shared connection-level inFlow that is shared
	// by all streams on that conn. It is nil for the inFlow on the conn
	// directly.
	conn *inFlow

	mu sync.Mutex
	// pendingData is the overall data which have been received but not been
	// consumed by applications.
	pendingData uint32
	// The amount of data the application has consumed but grpc has not sent
	// window update for them. Used to reduce window update frequency.
	pendingUpdate uint32
}

// onData is invoked when some data frame is received. It increments not only its
// own pendingData but also that of the associated connection-level flow.
func (f *inFlow) onData(n uint32) error {
	if n == 0 {
		return nil
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.pendingData+f.pendingUpdate+n > f.limit {
		return fmt.Errorf("recieved %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate+n, f.limit)
	}
	if f.conn != nil {
		if err := f.conn.onData(n); err != nil {
			return ConnectionErrorf("%v", err)
		}
	}
	f.pendingData += n
	return nil
}

// connOnRead updates the connection level states when the application consumes data.
func (f *inFlow) connOnRead(n uint32) uint32 {
	if n == 0 || f.conn != nil {
		return 0
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	f.pendingData -= n
	f.pendingUpdate += n
	if f.pendingUpdate >= f.limit/4 {
		ret := f.pendingUpdate
		f.pendingUpdate = 0
		return ret
	}
	return 0
}

// onRead is invoked when the application reads the data. It returns the window updates
// for both stream and connection level.
func (f *inFlow) onRead(n uint32) (swu, cwu uint32) {
	if n == 0 {
		return
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.pendingData == 0 {
		// pendingData has been adjusted by restoreConn.
		return
	}
	f.pendingData -= n
	f.pendingUpdate += n
	if f.pendingUpdate >= f.limit/4 {
		swu = f.pendingUpdate
		f.pendingUpdate = 0
	}
	cwu = f.conn.connOnRead(n)
	return
}

// restoreConn is invoked when a stream is terminated. It removes its stake in
// the connection-level flow and resets its own state.
func (f *inFlow) restoreConn() uint32 {
	if f.conn == nil {
		return 0
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	n := f.pendingData
	f.pendingData = 0
	f.pendingUpdate = 0
	return f.conn.connOnRead(n)
}
<|endoftext|>


<commit_before>package channel

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/common/response"
	"strconv"

	"github.com/koding/bongo"
	tigertonic "github.com/rcrowley/go-tigertonic"
)

func validateChannelRequest(c *models.Channel) error {
	if c.GroupName == "" {
		return models.ErrGroupNameIsNotSet
	}

	if c.Name == "" {
		return models.ErrNameIsNotSet
	}

	if c.CreatorId == 0 {
		return models.ErrCreatorIdIsNotSet
	}

	return nil
}

func Create(u *url.URL, h http.Header, req *models.Channel, context *models.Context) (int, http.Header, interface{}, error) {
	// only logged in users can create a channel
	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	// get group name from context
	req.GroupName = context.GroupName
	req.CreatorId = context.Client.Account.Id

	if req.PrivacyConstant == "" {
		req.PrivacyConstant = models.Channel_PRIVACY_PRIVATE

		// if group is koding, then make it public, because it was public before
		if req.GroupName == models.Channel_KODING_NAME {
			req.PrivacyConstant = models.Channel_PRIVACY_PUBLIC
		}
	}

	if req.TypeConstant == "" {
		req.TypeConstant = models.Channel_TYPE_TOPIC
	}

	if err := validateChannelRequest(req); err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.Create(); err != nil {
		return response.NewBadRequest(err)
	}

	if _, err := req.AddParticipant(req.CreatorId); err != nil {
		// channel create works as idempotent, that channel might have been created before
		if err != models.ErrAccountIsAlreadyInTheChannel {
			return response.NewBadRequest(err)
		}
	}

	cc := models.NewChannelContainer()
	if err := cc.PopulateWith(*req, context.Client.Account.Id); err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cc)
}

// List lists only topic channels
func List(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	c := models.NewChannel()
	q := request.GetQuery(u)
	// only list topic or linked topic channels
	if q.Type != models.Channel_TYPE_LINKED_TOPIC {
		q.Type = models.Channel_TYPE_TOPIC
	}

	// TODO refactor this function just to return channel ids
	// we cache wisely
	channelList, err := c.List(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return handleChannelListResponse(channelList, q)
}

func handleChannelListResponse(channelList []models.Channel, q *request.Query) (int, http.Header, interface{}, error) {
	cc := models.NewChannelContainers()
	if err := cc.Fetch(channelList, q); err != nil {
		return response.NewBadRequest(err)
	}
	cc.AddIsParticipant(q.AccountId)

	// TODO this should be in the channel cache by default
	cc.AddLastMessage(q.AccountId)

	return response.HandleResultAndError(cc, cc.Err())
}

// Search searchs database against given channel name
// but only returns topic channels
func Search(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)
	if q.Type != models.Channel_TYPE_LINKED_TOPIC {
		q.Type = models.Channel_TYPE_TOPIC
	}
	channelList, err := models.NewChannel().Search(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return handleChannelListResponse(channelList, q)
}

// ByName finds topics by their name
func ByName(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)

	if q.Type == "" {
		q.Type = models.Channel_TYPE_TOPIC
	}

	channel, err := models.NewChannel().ByName(q)
	if err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}

		if models.IsChannelLeafErr(err) {
			return http.StatusMovedPermanently,
				nil, nil,
				tigertonic.MovedPermanently{Err: err}
		}

		return response.NewBadRequest(err)
	}

	return handleChannelResponse(channel, q)
}

// ByParticipants finds private message channels by their participants
func ByParticipants(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	// only logged in users
	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	query := request.GetQuery(u)
	query.GroupName = context.GroupName

	participantsStr, ok := u.Query()["participants"]
	if !ok {
		return response.NewBadRequest(errors.New("participants not set"))
	}

	if len(participantsStr) == 0 {
		return response.NewBadRequest(errors.New("at least one participant is required"))
	}

	unify := make(map[string]interface{})

	// add current account to participants list
	unify[strconv.FormatInt(context.Client.Account.Id, 10)] = struct{}{}

	// remove duplicates from participants
	for i := range participantsStr {
		unify[participantsStr[i]] = struct{}{}
	}

	participants := make([]int64, 0)

	// convert strings to int64
	for participantStr := range unify {
		i, err := strconv.ParseInt(participantStr, 10, 64)
		if err != nil {
			return response.NewBadRequest(err)
		}

		participants = append(participants, i)
	}

	channels, err := models.NewChannel().ByParticipants(participants, query)
	if err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
	}

	cc := models.NewChannelContainers().
		PopulateWith(channels, context.Client.Account.Id).
		AddLastMessage(context.Client.Account.Id).
		AddUnreadCount(context.Client.Account.Id)

	return response.HandleResultAndError(cc, cc.Err())
}

func Get(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}
	q := request.GetQuery(u)

	c := models.NewChannel()
	if err := c.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	return handleChannelResponse(*c, q)
}

func handleChannelResponse(c models.Channel, q *request.Query) (int, http.Header, interface{}, error) {
	// add troll mode filter
	if c.MetaBits.Is(models.Troll) && !q.ShowExempt {
		return response.NewNotFound()
	}

	canOpen, err := c.CanOpen(q.AccountId)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if !canOpen {
		cp := models.NewChannelParticipant()
		cp.ChannelId = c.Id
		isInvited, err := cp.IsInvited(q.AccountId)
		if err != nil {
			return response.NewBadRequest(err)
		}

		if !isInvited {
			return response.NewAccessDenied(
				fmt.Errorf(
					"account (%d) tried to retrieve the unattended channel (%d)",
					q.AccountId,
					c.Id,
				),
			)
		}
	}

	cc := models.NewChannelContainer()

	if err := cc.Fetch(c.GetId(), q); err != nil {
		return response.NewBadRequest(err)
	}

	cc.AddIsParticipant(q.AccountId)

	// TODO this should be in the channel cache by default
	cc.AddLastMessage(q.AccountId)

	return response.HandleResultAndError(cc, cc.Err)
}

func CheckParticipation(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)
	if context.Client != nil && context.Client.Account != nil {
		q.AccountId = context.Client.Account.Id
	}

	if q.Type == "" || q.AccountId == 0 {
		return response.NewBadRequest(errors.New("type or accountid is not set"))
	}

	channel, err := models.NewChannel().ByName(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	res := models.NewCheckParticipationResponse()
	res.Channel = &channel
	res.Account = context.Client.Account
	if context.Client.Account != nil {
		res.AccountToken = context.Client.Account.Token
	}

	canOpen, err := channel.CanOpen(q.AccountId)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if !canOpen {
		return response.NewAccessDenied(
			fmt.Errorf(
				"account (%d) tried to retrieve the unattended private channel (%d)",
				q.AccountId,
				channel.Id,
			))
	}

	return response.NewOK(res)
}

func Delete(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {

	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.ById(id); err != nil {
		return response.NewBadRequest(err)
	}

	if req.TypeConstant == models.Channel_TYPE_GROUP {
		return response.NewBadRequest(errors.New("You can not delete group channel"))
	}
	if err := req.Delete(); err != nil {
		return response.NewBadRequest(err)
	}
	// yes it is deleted but not removed completely from our system
	return response.NewDeleted()
}

func Update(u *url.URL, h http.Header, req *models.Channel, c *models.Context) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}
	req.Id = id

	if req.Id == 0 {
		return response.NewBadRequest(err)
	}

	existingOne, err := models.Cache.Channel.ById(id)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if existingOne.CreatorId != c.Client.Account.Id {
		return response.NewBadRequest(errors.New("creatorId doesnt match"))
	}

	// only allow purpose, name and payload to be updated
	if req.Purpose != "" {
		existingOne.Purpose = req.Purpose
	}

	if req.Name != "" {
		existingOne.Name = req.Name
	}

	// some of the channels stores sparse data
	existingOne.Payload = req.Payload

	// update channel
	if err := existingOne.Update(); err != nil {
		return response.NewBadRequest(err)
	}

	// generate container data
	cc := models.NewChannelContainer()
	if err := cc.PopulateWith(*existingOne, c.Client.Account.Id); err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cc)
}
<commit_msg>Socialapi: change path name<commit_after>package channel

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/common/response"
	"strconv"

	"github.com/koding/bongo"
	tigertonic "github.com/rcrowley/go-tigertonic"
)

func validateChannelRequest(c *models.Channel) error {
	if c.GroupName == "" {
		return models.ErrGroupNameIsNotSet
	}

	if c.Name == "" {
		return models.ErrNameIsNotSet
	}

	if c.CreatorId == 0 {
		return models.ErrCreatorIdIsNotSet
	}

	return nil
}

func Create(u *url.URL, h http.Header, req *models.Channel, context *models.Context) (int, http.Header, interface{}, error) {
	// only logged in users can create a channel
	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	// get group name from context
	req.GroupName = context.GroupName
	req.CreatorId = context.Client.Account.Id

	if req.PrivacyConstant == "" {
		req.PrivacyConstant = models.Channel_PRIVACY_PRIVATE

		// if group is koding, then make it public, because it was public before
		if req.GroupName == models.Channel_KODING_NAME {
			req.PrivacyConstant = models.Channel_PRIVACY_PUBLIC
		}
	}

	if req.TypeConstant == "" {
		req.TypeConstant = models.Channel_TYPE_TOPIC
	}

	if err := validateChannelRequest(req); err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.Create(); err != nil {
		return response.NewBadRequest(err)
	}

	if _, err := req.AddParticipant(req.CreatorId); err != nil {
		// channel create works as idempotent, that channel might have been created before
		if err != models.ErrAccountIsAlreadyInTheChannel {
			return response.NewBadRequest(err)
		}
	}

	cc := models.NewChannelContainer()
	if err := cc.PopulateWith(*req, context.Client.Account.Id); err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cc)
}

// List lists only topic channels
func List(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	c := models.NewChannel()
	q := request.GetQuery(u)
	// only list topic or linked topic channels
	if q.Type != models.Channel_TYPE_LINKED_TOPIC {
		q.Type = models.Channel_TYPE_TOPIC
	}

	// TODO refactor this function just to return channel ids
	// we cache wisely
	channelList, err := c.List(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return handleChannelListResponse(channelList, q)
}

func handleChannelListResponse(channelList []models.Channel, q *request.Query) (int, http.Header, interface{}, error) {
	cc := models.NewChannelContainers()
	if err := cc.Fetch(channelList, q); err != nil {
		return response.NewBadRequest(err)
	}
	cc.AddIsParticipant(q.AccountId)

	// TODO this should be in the channel cache by default
	cc.AddLastMessage(q.AccountId)

	return response.HandleResultAndError(cc, cc.Err())
}

// Search searchs database against given channel name
// but only returns topic channels
func Search(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)
	if q.Type != models.Channel_TYPE_LINKED_TOPIC {
		q.Type = models.Channel_TYPE_TOPIC
	}
	channelList, err := models.NewChannel().Search(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return handleChannelListResponse(channelList, q)
}

// ByName finds topics by their name
func ByName(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)

	if q.Type == "" {
		q.Type = models.Channel_TYPE_TOPIC
	}

	channel, err := models.NewChannel().ByName(q)
	if err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}

		if models.IsChannelLeafErr(err) {
			return http.StatusMovedPermanently,
				nil, nil,
				tigertonic.MovedPermanently{Err: err}
		}

		return response.NewBadRequest(err)
	}

	return handleChannelResponse(channel, q)
}

// ByParticipants finds private message channels by their participants
func ByParticipants(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	// only logged in users
	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	query := request.GetQuery(u)
	query.GroupName = context.GroupName

	participantsStr, ok := u.Query()["id"]
	if !ok {
		return response.NewBadRequest(errors.New("participants not set"))
	}

	if len(participantsStr) == 0 {
		return response.NewBadRequest(errors.New("at least one participant is required"))
	}

	unify := make(map[string]interface{})

	// add current account to participants list
	unify[strconv.FormatInt(context.Client.Account.Id, 10)] = struct{}{}

	// remove duplicates from participants
	for i := range participantsStr {
		unify[participantsStr[i]] = struct{}{}
	}

	participants := make([]int64, 0)

	// convert strings to int64
	for participantStr := range unify {
		i, err := strconv.ParseInt(participantStr, 10, 64)
		if err != nil {
			return response.NewBadRequest(err)
		}

		participants = append(participants, i)
	}

	channels, err := models.NewChannel().ByParticipants(participants, query)
	if err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
	}

	cc := models.NewChannelContainers().
		PopulateWith(channels, context.Client.Account.Id).
		AddLastMessage(context.Client.Account.Id).
		AddUnreadCount(context.Client.Account.Id)

	return response.HandleResultAndError(cc, cc.Err())
}

func Get(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}
	q := request.GetQuery(u)

	c := models.NewChannel()
	if err := c.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	return handleChannelResponse(*c, q)
}

func handleChannelResponse(c models.Channel, q *request.Query) (int, http.Header, interface{}, error) {
	// add troll mode filter
	if c.MetaBits.Is(models.Troll) && !q.ShowExempt {
		return response.NewNotFound()
	}

	canOpen, err := c.CanOpen(q.AccountId)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if !canOpen {
		cp := models.NewChannelParticipant()
		cp.ChannelId = c.Id
		isInvited, err := cp.IsInvited(q.AccountId)
		if err != nil {
			return response.NewBadRequest(err)
		}

		if !isInvited {
			return response.NewAccessDenied(
				fmt.Errorf(
					"account (%d) tried to retrieve the unattended channel (%d)",
					q.AccountId,
					c.Id,
				),
			)
		}
	}

	cc := models.NewChannelContainer()

	if err := cc.Fetch(c.GetId(), q); err != nil {
		return response.NewBadRequest(err)
	}

	cc.AddIsParticipant(q.AccountId)

	// TODO this should be in the channel cache by default
	cc.AddLastMessage(q.AccountId)

	return response.HandleResultAndError(cc, cc.Err)
}

func CheckParticipation(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)
	if context.Client != nil && context.Client.Account != nil {
		q.AccountId = context.Client.Account.Id
	}

	if q.Type == "" || q.AccountId == 0 {
		return response.NewBadRequest(errors.New("type or accountid is not set"))
	}

	channel, err := models.NewChannel().ByName(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	res := models.NewCheckParticipationResponse()
	res.Channel = &channel
	res.Account = context.Client.Account
	if context.Client.Account != nil {
		res.AccountToken = context.Client.Account.Token
	}

	canOpen, err := channel.CanOpen(q.AccountId)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if !canOpen {
		return response.NewAccessDenied(
			fmt.Errorf(
				"account (%d) tried to retrieve the unattended private channel (%d)",
				q.AccountId,
				channel.Id,
			))
	}

	return response.NewOK(res)
}

func Delete(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {

	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.ById(id); err != nil {
		return response.NewBadRequest(err)
	}

	if req.TypeConstant == models.Channel_TYPE_GROUP {
		return response.NewBadRequest(errors.New("You can not delete group channel"))
	}
	if err := req.Delete(); err != nil {
		return response.NewBadRequest(err)
	}
	// yes it is deleted but not removed completely from our system
	return response.NewDeleted()
}

func Update(u *url.URL, h http.Header, req *models.Channel, c *models.Context) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}
	req.Id = id

	if req.Id == 0 {
		return response.NewBadRequest(err)
	}

	existingOne, err := models.Cache.Channel.ById(id)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if existingOne.CreatorId != c.Client.Account.Id {
		return response.NewBadRequest(errors.New("creatorId doesnt match"))
	}

	// only allow purpose, name and payload to be updated
	if req.Purpose != "" {
		existingOne.Purpose = req.Purpose
	}

	if req.Name != "" {
		existingOne.Name = req.Name
	}

	// some of the channels stores sparse data
	existingOne.Payload = req.Payload

	// update channel
	if err := existingOne.Update(); err != nil {
		return response.NewBadRequest(err)
	}

	// generate container data
	cc := models.NewChannelContainer()
	if err := cc.PopulateWith(*existingOne, c.Client.Account.Id); err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cc)
}
<|endoftext|>


<commit_before>// Copyright 2012-2017 Apcera Inc. All rights reserved.

package server

import (
	"fmt"
	"strings"

	"golang.org/x/crypto/bcrypt"
)

// User is for multiple accounts/users.
type User struct {
	Username    string       `json:"user"`
	Password    string       `json:"password"`
	Permissions *Permissions `json:"permissions"`
}

// clone performs a deep copy of the User struct, returning a new clone with
// all values copied.
func (u *User) clone() *User {
	if u == nil {
		return nil
	}
	clone := &User{}
	*clone = *u
	clone.Permissions = u.Permissions.clone()
	return clone
}

// Permissions are the allowed subjects on a per
// publish or subscribe basis.
type Permissions struct {
	Publish   []string `json:"publish"`
	Subscribe []string `json:"subscribe"`
}

// clone performs a deep copy of the Permissions struct, returning a new clone
// with all values copied.
func (p *Permissions) clone() *Permissions {
	if p == nil {
		return nil
	}
	clone := &Permissions{}
	if p.Publish != nil {
		clone.Publish = make([]string, len(p.Publish))
		copy(clone.Publish, p.Publish)
	}
	if p.Subscribe != nil {
		clone.Subscribe = make([]string, len(p.Subscribe))
		copy(clone.Subscribe, p.Subscribe)
	}
	return clone
}

// configureAuthorization will do any setup needed for authorization.
// Lock is assumed held.
func (s *Server) configureAuthorization() {
	if s.opts == nil {
		return
	}

	// Snapshot server options.
	opts := s.getOpts()

	// Check for multiple users first
	// This just checks and sets up the user map if we have multiple users.
	if opts.Users != nil {
		s.users = make(map[string]*User)
		for _, u := range opts.Users {
			s.users[u.Username] = u
		}
		s.info.AuthRequired = true
	} else if opts.Username != "" || opts.Authorization != "" {
		s.info.AuthRequired = true
	} else {
		s.users = nil
		s.info.AuthRequired = false
	}
}

// checkAuthorization will check authorization based on client type and
// return boolean indicating if client is authorized.
func (s *Server) checkAuthorization(c *client) bool {
	switch c.typ {
	case CLIENT:
		return s.isClientAuthorized(c)
	case ROUTER:
		return s.isRouterAuthorized(c)
	default:
		return false
	}
}

// isClientAuthorized will check the client against the proper authorization method and data.
// This could be token or username/password based.
func (s *Server) isClientAuthorized(c *client) bool {
	// Snapshot server options.
	opts := s.getOpts()

	// Check multiple users first, then token, then single user/pass.
	if s.users != nil {
		user, ok := s.users[c.opts.Username]
		if !ok {
			return false
		}
		ok = comparePasswords(user.Password, c.opts.Password)
		// If we are authorized, register the user which will properly setup any permissions
		// for pub/sub authorizations.
		if ok {
			c.RegisterUser(user)
		}
		return ok

	} else if opts.Authorization != "" {
		return comparePasswords(opts.Authorization, c.opts.Authorization)

	} else if opts.Username != "" {
		if opts.Username != c.opts.Username {
			return false
		}
		return comparePasswords(opts.Password, c.opts.Password)
	}

	return true
}

// checkRouterAuth checks optional router authorization which can be nil or username/password.
func (s *Server) isRouterAuthorized(c *client) bool {
	// Snapshot server options.
	opts := s.getOpts()

	if opts.Cluster.Username != c.opts.Username {
		return false
	}
	return comparePasswords(opts.Cluster.Password, c.opts.Password)
}

// removeUnauthorizedSubs removes any subscriptions the client has that are no
// longer authorized, e.g. due to a config reload.
func (s *Server) removeUnauthorizedSubs(c *client) {
	c.mu.Lock()
	if c.perms == nil {
		c.mu.Unlock()
		return
	}

	subs := make(map[string]*subscription, len(c.subs))
	for sid, sub := range c.subs {
		subs[sid] = sub
	}
	c.mu.Unlock()

	for sid, sub := range subs {
		if !c.canSubscribe(sub.subject) {
			_ = s.sl.Remove(sub)
			c.mu.Lock()
			delete(c.subs, sid)
			c.mu.Unlock()
			c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q (sid %s)",
				sub.subject, sub.sid))
			s.Noticef("Removed sub %q for user %q - not authorized",
				string(sub.subject), c.opts.Username)
		}
	}
}

// Support for bcrypt stored passwords and tokens.
const bcryptPrefix = "$2a$"

// isBcrypt checks whether the given password or token is bcrypted.
func isBcrypt(password string) bool {
	return strings.HasPrefix(password, bcryptPrefix)
}

func comparePasswords(serverPassword, clientPassword string) bool {
	// Check to see if the server password is a bcrypt hash
	if isBcrypt(serverPassword) {
		if err := bcrypt.CompareHashAndPassword([]byte(serverPassword), []byte(clientPassword)); err != nil {
			return false
		}
	} else if serverPassword != clientPassword {
		return false
	}
	return true
}
<commit_msg>Pass auth check if cluster username is not set<commit_after>// Copyright 2012-2017 Apcera Inc. All rights reserved.

package server

import (
	"fmt"
	"strings"

	"golang.org/x/crypto/bcrypt"
)

// User is for multiple accounts/users.
type User struct {
	Username    string       `json:"user"`
	Password    string       `json:"password"`
	Permissions *Permissions `json:"permissions"`
}

// clone performs a deep copy of the User struct, returning a new clone with
// all values copied.
func (u *User) clone() *User {
	if u == nil {
		return nil
	}
	clone := &User{}
	*clone = *u
	clone.Permissions = u.Permissions.clone()
	return clone
}

// Permissions are the allowed subjects on a per
// publish or subscribe basis.
type Permissions struct {
	Publish   []string `json:"publish"`
	Subscribe []string `json:"subscribe"`
}

// clone performs a deep copy of the Permissions struct, returning a new clone
// with all values copied.
func (p *Permissions) clone() *Permissions {
	if p == nil {
		return nil
	}
	clone := &Permissions{}
	if p.Publish != nil {
		clone.Publish = make([]string, len(p.Publish))
		copy(clone.Publish, p.Publish)
	}
	if p.Subscribe != nil {
		clone.Subscribe = make([]string, len(p.Subscribe))
		copy(clone.Subscribe, p.Subscribe)
	}
	return clone
}

// configureAuthorization will do any setup needed for authorization.
// Lock is assumed held.
func (s *Server) configureAuthorization() {
	if s.opts == nil {
		return
	}

	// Snapshot server options.
	opts := s.getOpts()

	// Check for multiple users first
	// This just checks and sets up the user map if we have multiple users.
	if opts.Users != nil {
		s.users = make(map[string]*User)
		for _, u := range opts.Users {
			s.users[u.Username] = u
		}
		s.info.AuthRequired = true
	} else if opts.Username != "" || opts.Authorization != "" {
		s.info.AuthRequired = true
	} else {
		s.users = nil
		s.info.AuthRequired = false
	}
}

// checkAuthorization will check authorization based on client type and
// return boolean indicating if client is authorized.
func (s *Server) checkAuthorization(c *client) bool {
	switch c.typ {
	case CLIENT:
		return s.isClientAuthorized(c)
	case ROUTER:
		return s.isRouterAuthorized(c)
	default:
		return false
	}
}

// isClientAuthorized will check the client against the proper authorization method and data.
// This could be token or username/password based.
func (s *Server) isClientAuthorized(c *client) bool {
	// Snapshot server options.
	opts := s.getOpts()

	// Check multiple users first, then token, then single user/pass.
	if s.users != nil {
		user, ok := s.users[c.opts.Username]
		if !ok {
			return false
		}
		ok = comparePasswords(user.Password, c.opts.Password)
		// If we are authorized, register the user which will properly setup any permissions
		// for pub/sub authorizations.
		if ok {
			c.RegisterUser(user)
		}
		return ok

	} else if opts.Authorization != "" {
		return comparePasswords(opts.Authorization, c.opts.Authorization)

	} else if opts.Username != "" {
		if opts.Username != c.opts.Username {
			return false
		}
		return comparePasswords(opts.Password, c.opts.Password)
	}

	return true
}

// checkRouterAuth checks optional router authorization which can be nil or username/password.
func (s *Server) isRouterAuthorized(c *client) bool {
	// Snapshot server options.
	opts := s.getOpts()

	if opts.Cluster.Username == "" {
		return true
	}

	if opts.Cluster.Username != c.opts.Username {
		return false
	}
	return comparePasswords(opts.Cluster.Password, c.opts.Password)
}

// removeUnauthorizedSubs removes any subscriptions the client has that are no
// longer authorized, e.g. due to a config reload.
func (s *Server) removeUnauthorizedSubs(c *client) {
	c.mu.Lock()
	if c.perms == nil {
		c.mu.Unlock()
		return
	}

	subs := make(map[string]*subscription, len(c.subs))
	for sid, sub := range c.subs {
		subs[sid] = sub
	}
	c.mu.Unlock()

	for sid, sub := range subs {
		if !c.canSubscribe(sub.subject) {
			_ = s.sl.Remove(sub)
			c.mu.Lock()
			delete(c.subs, sid)
			c.mu.Unlock()
			c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q (sid %s)",
				sub.subject, sub.sid))
			s.Noticef("Removed sub %q for user %q - not authorized",
				string(sub.subject), c.opts.Username)
		}
	}
}

// Support for bcrypt stored passwords and tokens.
const bcryptPrefix = "$2a$"

// isBcrypt checks whether the given password or token is bcrypted.
func isBcrypt(password string) bool {
	return strings.HasPrefix(password, bcryptPrefix)
}

func comparePasswords(serverPassword, clientPassword string) bool {
	// Check to see if the server password is a bcrypt hash
	if isBcrypt(serverPassword) {
		if err := bcrypt.CompareHashAndPassword([]byte(serverPassword), []byte(clientPassword)); err != nil {
			return false
		}
	} else if serverPassword != clientPassword {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package support

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	neturl "net/url"
	"strings"

	docker "github.com/giantswarm/hijack-stream-support/docker"
)

type HijackHttpOptions struct {
	Method             string
	Url                string
	Host               string // If set, this will be passed as `Host` header to the request.
	DockerTermProtocol bool
	InputStream        io.Reader
	ErrorStream        io.Writer
	OutputStream       io.Writer
	Data               interface{}
	Header             http.Header
	Log                docker.Logger
}

var (
	ErrMissingMethod = errors.New("Method not set")
	ErrMissingUrl    = errors.New("Url not set")
)

// HijackHttpRequest performs an HTTP  request with given method, url and data and hijacks the request (after a successful connection) to stream
// data from/to the given input, output and error streams.
func HijackHttpRequest(options HijackHttpOptions) error {
	if options.Log == nil {
		// Make sure there is always a logger
		options.Log = &logIgnore{}
	}
	if options.Method == "" {
		return ErrMissingMethod
	}
	if options.Url == "" {
		return ErrMissingUrl
	}

	req, err := createHijackHttpRequest(options)
	if err != nil {
		return err
	}

	// Parse URL for endpoint data
	ep, err := neturl.Parse(options.Url)
	if err != nil {
		return err
	}

	protocol := ep.Scheme
	address := ep.Path
	if protocol != "unix" {
		protocol = "tcp"
		address = ep.Host
		if !strings.Contains(address, ":") {
			if ep.Scheme == "https" {
				address = address + ":443"
			} else {
				address = address + ":80"
			}
		}
	}

	// Dial the server
	var dial net.Conn
	fmt.Printf("Dialing %s %s\n", protocol, address)
	if protocol == "https" {
		config := &tls.Config{}
		dial, err = docker.TLSDial(protocol, address, config)
		if err != nil {
			fmt.Printf("TLS Dialing %s %s failed %#v\n", protocol, address, err)
			return err
		}
	} else {
		dial, err = net.Dial(protocol, address)
		if err != nil {
			fmt.Printf("Dialing %s %s failed %#v\n", protocol, address, err)
			return err
		}
	}

	// Start initial HTTP connection
	clientconn := httputil.NewClientConn(dial, nil)
	defer clientconn.Close()

	clientconn.Do(req)

	// Hijack HTTP connection
	rwc, br := clientconn.Hijack()
	defer rwc.Close()

	// Stream data
	return streamData(rwc, br, options)
}

// createHijackHttpRequest creates an upgradable HTTP request according to the given options
func createHijackHttpRequest(options HijackHttpOptions) (*http.Request, error) {
	var params io.Reader
	if options.Data != nil {
		buf, err := json.Marshal(options.Data)
		if err != nil {
			return nil, err
		}
		params = bytes.NewBuffer(buf)
	}

	req, err := http.NewRequest(options.Method, options.Url, params)
	if err != nil {
		return nil, err
	}
	if options.Header != nil {
		for k, values := range options.Header {
			req.Header.Del(k)
			for _, v := range values {
				req.Header.Set(k, v)
			}
		}
	}
	req.Header.Set("Content-Type", "text/plain")
	req.Header.Set("Connection", "Upgrade")
	req.Header.Set("Upgrade", "tcp")
	if options.Host != "" {
		req.Host = options.Host
	}
	return req, nil
}

// streamData copies both input/output/error streams to/from the hijacked streams
func streamData(rwc io.Writer, br io.Reader, options HijackHttpOptions) error {
	errsIn := make(chan error, 1)
	errsOut := make(chan error, 1)
	exit := make(chan bool)

	go func() {
		defer close(exit)
		defer close(errsOut)
		var err error
		stdout := options.OutputStream
		if stdout == nil {
			stdout = ioutil.Discard
		}
		stderr := options.ErrorStream
		if stderr == nil {
			stderr = ioutil.Discard
		}
		if !options.DockerTermProtocol {
			// When TTY is ON, use regular copy
			_, err = io.Copy(stdout, br)
		} else {
			_, err = docker.StdCopy(stdout, stderr, br, options.Log)
		}
		errsOut <- err
	}()
	go func() {
		defer close(errsIn)
		var err error
		in := options.InputStream
		if in != nil {
			_, err = io.Copy(rwc, in)
		}
		if err := rwc.(closeWriter).CloseWrite(); err != nil {
			options.Log.Debugf("CloseWrite failed %#v", err)
		}
		errsIn <- err
	}()
	<-exit
	select {
	case err := <-errsOut:
		return err
	case err := <-errsIn:
		return err
	}
}

// ----------------------------------------------
// private interface supporting CloseWrite calls.

type closeWriter interface {
	CloseWrite() error
}

// ----------------------------------------------
// Helper to ignore debug los in case we got no logger

type logIgnore struct {
}

func (this *logIgnore) Debugf(msg string, args ...interface{}) {
	// Ignore the log message
}
<commit_msg>Disabled setting Upgrade http header<commit_after>package support

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	neturl "net/url"
	"strings"

	docker "github.com/giantswarm/hijack-stream-support/docker"
)

type HijackHttpOptions struct {
	Method             string
	Url                string
	Host               string // If set, this will be passed as `Host` header to the request.
	DockerTermProtocol bool
	InputStream        io.Reader
	ErrorStream        io.Writer
	OutputStream       io.Writer
	Data               interface{}
	Header             http.Header
	Log                docker.Logger
}

var (
	ErrMissingMethod = errors.New("Method not set")
	ErrMissingUrl    = errors.New("Url not set")
)

// HijackHttpRequest performs an HTTP  request with given method, url and data and hijacks the request (after a successful connection) to stream
// data from/to the given input, output and error streams.
func HijackHttpRequest(options HijackHttpOptions) error {
	if options.Log == nil {
		// Make sure there is always a logger
		options.Log = &logIgnore{}
	}
	if options.Method == "" {
		return ErrMissingMethod
	}
	if options.Url == "" {
		return ErrMissingUrl
	}

	req, err := createHijackHttpRequest(options)
	if err != nil {
		return err
	}

	// Parse URL for endpoint data
	ep, err := neturl.Parse(options.Url)
	if err != nil {
		return err
	}

	protocol := ep.Scheme
	address := ep.Path
	if protocol != "unix" {
		protocol = "tcp"
		address = ep.Host
		if !strings.Contains(address, ":") {
			if ep.Scheme == "https" {
				address = address + ":443"
			} else {
				address = address + ":80"
			}
		}
	}

	// Dial the server
	var dial net.Conn
	//fmt.Printf("Dialing %s %s\n", protocol, address)
	if protocol == "https" {
		config := &tls.Config{}
		dial, err = docker.TLSDial(protocol, address, config)
		if err != nil {
			fmt.Printf("TLS Dialing %s %s failed %#v\n", protocol, address, err)
			return err
		}
	} else {
		dial, err = net.Dial(protocol, address)
		if err != nil {
			fmt.Printf("Dialing %s %s failed %#v\n", protocol, address, err)
			return err
		}
	}

	// Start initial HTTP connection
	clientconn := httputil.NewClientConn(dial, nil)
	defer clientconn.Close()

	clientconn.Do(req)

	// Hijack HTTP connection
	rwc, br := clientconn.Hijack()
	defer rwc.Close()

	// Stream data
	return streamData(rwc, br, options)
}

// createHijackHttpRequest creates an upgradable HTTP request according to the given options
func createHijackHttpRequest(options HijackHttpOptions) (*http.Request, error) {
	var params io.Reader
	if options.Data != nil {
		buf, err := json.Marshal(options.Data)
		if err != nil {
			return nil, err
		}
		params = bytes.NewBuffer(buf)
	}

	req, err := http.NewRequest(options.Method, options.Url, params)
	if err != nil {
		return nil, err
	}
	if options.Header != nil {
		for k, values := range options.Header {
			req.Header.Del(k)
			for _, v := range values {
				req.Header.Set(k, v)
			}
		}
	}
	req.Header.Set("Content-Type", "text/plain")
	//req.Header.Set("Connection", "Upgrade")
	//req.Header.Set("Upgrade", "tcp")
	if options.Host != "" {
		req.Host = options.Host
	}
	return req, nil
}

// streamData copies both input/output/error streams to/from the hijacked streams
func streamData(rwc io.Writer, br io.Reader, options HijackHttpOptions) error {
	errsIn := make(chan error, 1)
	errsOut := make(chan error, 1)
	exit := make(chan bool)

	go func() {
		defer close(exit)
		defer close(errsOut)
		var err error
		stdout := options.OutputStream
		if stdout == nil {
			stdout = ioutil.Discard
		}
		stderr := options.ErrorStream
		if stderr == nil {
			stderr = ioutil.Discard
		}
		if !options.DockerTermProtocol {
			// When TTY is ON, use regular copy
			_, err = io.Copy(stdout, br)
		} else {
			_, err = docker.StdCopy(stdout, stderr, br, options.Log)
		}
		errsOut <- err
	}()
	go func() {
		defer close(errsIn)
		var err error
		in := options.InputStream
		if in != nil {
			_, err = io.Copy(rwc, in)
		}
		if err := rwc.(closeWriter).CloseWrite(); err != nil {
			options.Log.Debugf("CloseWrite failed %#v", err)
		}
		errsIn <- err
	}()
	<-exit
	select {
	case err := <-errsOut:
		return err
	case err := <-errsIn:
		return err
	}
}

// ----------------------------------------------
// private interface supporting CloseWrite calls.

type closeWriter interface {
	CloseWrite() error
}

// ----------------------------------------------
// Helper to ignore debug los in case we got no logger

type logIgnore struct {
}

func (this *logIgnore) Debugf(msg string, args ...interface{}) {
	// Ignore the log message
}
<|endoftext|>


<commit_before>package oauth2

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"time"
)

type User struct {
	Login string
}

type Client struct {
	ID, Secret   string
	Name         string
	RedirectURI  string
	Internal     bool
	Confidential bool
}

func NewClient(name, redirectURI string, confidential, internal bool) (*Client, error) {
	c := Client{
		Name:         name,
		RedirectURI:  redirectURI,
		Confidential: confidential,
		Internal:     internal,
	}

	if b, err := secureRandomBytes(32); err != nil {
		return nil, err
	} else {
		c.ID = hex.EncodeToString(b)
	}

	if b, err := secureRandomBytes(64); err != nil {
		return nil, err
	} else {
		c.Secret = hex.EncodeToString(b)
	}

	return &c, nil
}

type Authorization struct {
	Client *Client `json:"-"`
	User   *User   `json:"-"`

	Code      string    `json:"-"`
	CreatedAt time.Time `json:"-"`

	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token,omitempty"`
	Scope        string `json:"scope"`
}

func NewAuthorization(c *Client, u *User, scope string, code bool) (*Authorization, error) {
	a := Authorization{
		Client:    c,
		User:      u,
		CreatedAt: time.Now().UTC(),
		ExpiresIn: 3600,
		Scope:     scope,
	}

	if code {
		if b, err := secureRandomBytes(16); err != nil {
			return nil, err
		} else {
			a.Code = base64.URLEncoding.EncodeToString(b)
		}
	}

	if b, err := secureRandomBytes(64); err != nil {
		return nil, err
	} else {
		a.AccessToken = base64.URLEncoding.EncodeToString(b)
	}

	if c.Confidential {
		if b, err := secureRandomBytes(128); err != nil {
			return nil, err
		} else {
			a.RefreshToken = base64.URLEncoding.EncodeToString(b)
		}
	}

	return &a, nil
}

func secureRandomBytes(bytes uint) ([]byte, error) {
	r := make([]byte, bytes)
	_, err := rand.Read(r)
	return r, err
}

type Scope struct {
	ID   string
	Desc string
}
<commit_msg>60 days for access tokens<commit_after>package oauth2

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"time"
)

type User struct {
	Login string
}

type Client struct {
	ID, Secret   string
	Name         string
	RedirectURI  string
	Internal     bool
	Confidential bool
}

func NewClient(name, redirectURI string, confidential, internal bool) (*Client, error) {
	c := Client{
		Name:         name,
		RedirectURI:  redirectURI,
		Confidential: confidential,
		Internal:     internal,
	}

	if b, err := secureRandomBytes(32); err != nil {
		return nil, err
	} else {
		c.ID = hex.EncodeToString(b)
	}

	if b, err := secureRandomBytes(64); err != nil {
		return nil, err
	} else {
		c.Secret = hex.EncodeToString(b)
	}

	return &c, nil
}

type Authorization struct {
	Client *Client `json:"-"`
	User   *User   `json:"-"`

	Code      string    `json:"-"`
	CreatedAt time.Time `json:"-"`

	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token,omitempty"`
	Scope        string `json:"scope"`
}

func NewAuthorization(c *Client, u *User, scope string, code bool) (*Authorization, error) {
	a := Authorization{
		Client:    c,
		User:      u,
		CreatedAt: time.Now().UTC(),
		ExpiresIn: 24 * time.Hour * 60,
		Scope:     scope,
	}

	if code {
		if b, err := secureRandomBytes(16); err != nil {
			return nil, err
		} else {
			a.Code = base64.URLEncoding.EncodeToString(b)
		}
	}

	if b, err := secureRandomBytes(64); err != nil {
		return nil, err
	} else {
		a.AccessToken = base64.URLEncoding.EncodeToString(b)
	}

	if c.Confidential {
		if b, err := secureRandomBytes(128); err != nil {
			return nil, err
		} else {
			a.RefreshToken = base64.URLEncoding.EncodeToString(b)
		}
	}

	return &a, nil
}

func secureRandomBytes(bytes uint) ([]byte, error) {
	r := make([]byte, bytes)
	_, err := rand.Read(r)
	return r, err
}

type Scope struct {
	ID   string
	Desc string
}
<|endoftext|>