File fix-securecookie-value-too-long.patch of Package gangway

diff --git a/Gopkg.lock b/Gopkg.lock
index c36883f..af6de07 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -193,6 +193,7 @@
   input-imports = [
     "github.com/dgrijalva/jwt-go",
     "github.com/ghodss/yaml",
+    "github.com/gorilla/securecookie",
     "github.com/gorilla/sessions",
     "github.com/justinas/alice",
     "github.com/kelseyhightower/envconfig",
diff --git a/Makefile b/Makefile
index a467ef5..8f61f96 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ setup:
 	go get -u github.com/golang/dep/cmd/dep
 	go get -u github.com/mjibson/esc/...
 
-check: test vet gofmt staticcheck unused misspell
+check: test vet gofmt staticcheck misspell
 
 deps:
 	dep ensure -v
@@ -49,11 +49,7 @@ test:
 
 staticcheck:
 	@go get honnef.co/go/tools/cmd/staticcheck
-	staticcheck $(PKGS)
-
-unused:
-	@go get honnef.co/go/tools/cmd/unused
-	unused -exported $(PKGS)
+	staticcheck -unused.whole-program $(PKGS)
 
 misspell:
 	@go get github.com/client9/misspell/cmd/misspell
diff --git a/internal/session/session.go b/internal/session/session.go
index 93d3fc8..8c76260 100644
--- a/internal/session/session.go
+++ b/internal/session/session.go
@@ -16,23 +16,21 @@ package session
 
 import (
 	"crypto/sha256"
-	"net/http"
-
-	"github.com/gorilla/sessions"
 	"golang.org/x/crypto/pbkdf2"
+	"net/http"
 )
 
 const salt = "MkmfuPNHnZBBivy0L0aW"
 
 // Session defines a Gangway session
 type Session struct {
-	Session *sessions.CookieStore
+	Session *CustomCookieStore
 }
 
 // New inits a Session with CookieStore
 func New(sessionSecurityKey string) *Session {
 	return &Session{
-		Session: sessions.NewCookieStore(generateSessionKeys(sessionSecurityKey)),
+		Session: NewCustomCookieStore(generateSessionKeys(sessionSecurityKey)),
 	}
 }
 
diff --git a/internal/session/store.go b/internal/session/store.go
new file mode 100644
index 0000000..9b930c0
--- /dev/null
+++ b/internal/session/store.go
@@ -0,0 +1,135 @@
+// Copyright © 2019 Heptio
+// 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 session
+
+import (
+	"fmt"
+	"github.com/gorilla/securecookie"
+	"github.com/gorilla/sessions"
+	"net/http"
+)
+
+// The CustomCookieStore automatically splits cookies with length greater than maxCookieLength into multiple smaller cookies.
+// The motivation is the browsers' 4KB limit on cookies, which for instance causes problems for large id_tokens in azure.
+
+const (
+	// Cookies are limited to 4kb including the length of the cookie name,
+	// the cookie name can be up to 256 bytes
+	maxCookieLength = 3840
+)
+
+type CustomCookieStore struct {
+	*sessions.CookieStore
+}
+
+// Set secureCookie maxLength to an arbitrary (20x4kb) high value since we are no longer limited
+func NewCustomCookieStore(keyPairs ...[]byte) *CustomCookieStore {
+	cookieStore := sessions.NewCookieStore(keyPairs...)
+	for _, codec := range cookieStore.Codecs {
+		cookie := codec.(*securecookie.SecureCookie)
+		cookie.MaxLength(81920)
+	}
+	return &CustomCookieStore{cookieStore}
+}
+
+func (s *CustomCookieStore) Get(r *http.Request, name string) (*sessions.Session, error) {
+	return sessions.GetRegistry(r).Get(s, name)
+}
+
+// In contrast to default implementation, the session values can be partitioned into
+// multiple cookies.
+// The original cookie is split/joined in its encoded form
+func (s *CustomCookieStore) New(r *http.Request, name string) (*sessions.Session, error) {
+	session := sessions.NewSession(s, name)
+	opts := *s.Options
+	session.Options = &opts
+	session.IsNew = true
+	cookie := joinSectionCookies(r, name)
+	var err error
+	if len(cookie) > 0 {
+		err = securecookie.DecodeMulti(name, cookie, &session.Values, s.Codecs...)
+		if err == nil {
+			session.IsNew = false
+		}
+	}
+	return session, err
+}
+
+// If the cookie length is > maxCookieLength, its value is split into multiple cookies
+// fitting into the maxCookieLength limit.
+// The resulting section cookies get their index appended to the name.
+func (s *CustomCookieStore) Save(r *http.Request, w http.ResponseWriter,
+	session *sessions.Session) error {
+
+	cookie, err := securecookie.EncodeMulti(session.Name(), session.Values,
+		s.Codecs...)
+	if err != nil {
+		return err
+	}
+
+	sectionCookies := splitCookie(cookie)
+	// With a singular section the name is unchanged
+	if len(sectionCookies) == 1 {
+		cookieName := session.Name()
+		http.SetCookie(w, sessions.NewCookie(cookieName, sectionCookies[0], session.Options))
+		return nil
+	}
+
+	for i, value := range sectionCookies {
+		cookieName := buildSectionCookieName(session.Name(), i)
+		http.SetCookie(w, sessions.NewCookie(cookieName, value, session.Options))
+	}
+	return nil
+}
+
+// joinCookies concatenates the values of all matching cookies and returns the original, encoded cookievalue string.
+func joinSectionCookies(r *http.Request, name string) string {
+
+	// Exact match without index means only a single cookie exists
+	if c, err := r.Cookie(name); err == nil {
+		return c.Value
+	}
+
+	var joinedValue string
+	for i := 0; true; i++ {
+		cookieName := buildSectionCookieName(name, i)
+		if c, err := r.Cookie(cookieName); err == nil {
+			joinedValue += c.Value
+		} else {
+			break
+		}
+	}
+	return joinedValue
+}
+
+// splitCookie splits the original encoded cookie value into a slice of cookies which
+// fit within the 4kb cookie limit indexing the cookies from 0
+func splitCookie(cookieValue string) []string {
+	var sectionCookies []string
+	valueBytes := []byte(cookieValue)
+
+	for len(valueBytes) > 0 {
+		length := len(valueBytes)
+		if length > maxCookieLength {
+			length = maxCookieLength
+		}
+		sectionCookies = append(sectionCookies, string(valueBytes[:length]))
+		valueBytes = valueBytes[length:]
+	}
+	return sectionCookies
+}
+
+func buildSectionCookieName(name string, index int) string {
+	return fmt.Sprintf("%s_%d", name, index)
+}
diff --git a/internal/session/store_test.go b/internal/session/store_test.go
new file mode 100644
index 0000000..c010f8a
--- /dev/null
+++ b/internal/session/store_test.go
@@ -0,0 +1,154 @@
+// Copyright © 2019 Heptio
+// 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 session
+
+import (
+	"fmt"
+	"github.com/gorilla/sessions"
+	log "github.com/sirupsen/logrus"
+	"math"
+	"math/rand"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestJoinSectionCookies(t *testing.T) {
+	var originalValue string
+	var value string
+	cookies := buildRandomCookies(2, 3800, "test_%d")
+	buildRequestWithCookies(cookies, func(cookies []*http.Cookie, r *http.Request) {
+		for _, c := range cookies {
+			originalValue += c.Value
+		}
+		value = joinSectionCookies(r, "test")
+	})
+	if value != originalValue {
+		t.Errorf("joinSectionCookies value incorrect: \n value: %s \n originalValue: %s", value, originalValue)
+	}
+}
+
+func TestJoinSectionCookiesSingle(t *testing.T) {
+	var originalValue string
+	var value string
+	cookies := buildRandomCookies(1, 2000, "test_%d")
+	buildRequestWithCookies(cookies, func(cookies []*http.Cookie, r *http.Request) {
+		for _, c := range cookies {
+			originalValue += c.Value
+		}
+		value = joinSectionCookies(r, "test")
+	})
+	if value != originalValue {
+		t.Errorf("joinSectionCookies value incorrect: \n value: %s \n originalValue: %s", value, originalValue)
+	}
+}
+
+func TestSplitCookie(t *testing.T) {
+	cookieLength := 8000
+	originalValue := randStringBytesRmndr(cookieLength)
+	sectionCookies := splitCookie(originalValue)
+	expectedCount := int(math.Ceil((float64(cookieLength) / maxCookieLength)))
+	if len(sectionCookies) != expectedCount {
+		t.Errorf("splitCookie count incorrect: \n count: %d \n expectedCount: %d", len(sectionCookies), expectedCount)
+	}
+	value := strings.Join(sectionCookies, "")
+	if value != originalValue {
+		t.Errorf("splitCookie value incorrect: \n value: %s \n originalValue: %s", value, originalValue)
+	}
+}
+
+func TestSplitCookieSingle(t *testing.T) {
+	cookieLength := 2000
+	originalValue := randStringBytesRmndr(cookieLength)
+	sectionCookies := splitCookie(originalValue)
+	expectedCount := int(math.Ceil((float64(cookieLength) / maxCookieLength)))
+	if len(sectionCookies) != expectedCount {
+		t.Errorf("splitCookie count incorrect: \n count: %d \n expectedCount: %d", len(sectionCookies), expectedCount)
+	}
+}
+
+func TestSplitCookieSize(t *testing.T) {
+	cookieLength := 10000
+	originalValue := randStringBytesRmndr(cookieLength)
+	sectionCookies := splitCookie(originalValue)
+	for _, s := range sectionCookies {
+		if len(s) > maxCookieLength {
+			t.Errorf("sectionCookie length over limit: \n length: %d", len(s))
+		}
+	}
+}
+
+func TestSplitAndJoin(t *testing.T) {
+	cookieLength := 10000
+	originalValue := randStringBytesRmndr(cookieLength)
+	sectionCookies := splitCookie(originalValue)
+	cookies := buildCookiesFromValues(sectionCookies, "test_%d")
+	var value string
+	buildRequestWithCookies(cookies, func(cookies []*http.Cookie, r *http.Request) {
+		value = joinSectionCookies(r, "test")
+	})
+	if value != originalValue {
+		t.Errorf("SplitAndJoin value incorrect: \n value: %s \n originalValue: %s", value, originalValue)
+	}
+}
+
+// Utility
+
+type handleReq func([]*http.Cookie, *http.Request)
+
+func buildRequestWithCookies(cookies []*http.Cookie, fn handleReq) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		for _, cookie := range cookies {
+			r.AddCookie(cookie)
+		}
+		fn(cookies, r)
+	}))
+	defer ts.Close()
+	_, err := http.Get(ts.URL)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func buildRandomCookies(cookieCount int, cookieLength int, cookieName string) []*http.Cookie {
+	sessionOptions := &sessions.Options{}
+	var cookies []*http.Cookie
+	for i := 0; i < cookieCount; i++ {
+		value := randStringBytesRmndr(cookieLength)
+		cookie := sessions.NewCookie(fmt.Sprintf(cookieName, i), value, sessionOptions)
+		cookies = append(cookies, cookie)
+	}
+	return cookies
+}
+
+func buildCookiesFromValues(values []string, cookieName string) []*http.Cookie {
+	sessionOptions := &sessions.Options{}
+	var cookies []*http.Cookie
+	for i, value := range values {
+		cookie := sessions.NewCookie(fmt.Sprintf(cookieName, i), value, sessionOptions)
+		cookies = append(cookies, cookie)
+	}
+	return cookies
+}
+
+const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+func randStringBytesRmndr(n int) string {
+	b := make([]byte, n)
+	for i := range b {
+		b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
+	}
+	return string(b)
+}
openSUSE Build Service is sponsored by