Implement ClearSession for cookie SessionStore

This commit is contained in:
Joel Speed 2019-05-07 00:20:36 +01:00
parent 8b3a3853eb
commit 15a2cf8b9e
No known key found for this signature in database
GPG Key ID: 6E80578D6751DEFB
3 changed files with 99 additions and 13 deletions

34
pkg/cookies/cookies.go Normal file
View File

@ -0,0 +1,34 @@
package cookies
import (
"net"
"net/http"
"strings"
"time"
"github.com/pusher/oauth2_proxy/logger"
)
// MakeCookie constructs a cookie from the given parameters,
// discovering the domain from the request if not specified.
func MakeCookie(req *http.Request, name string, value string, path string, domain string, httpOnly bool, secure bool, expiration time.Duration, now time.Time) *http.Cookie {
if domain != "" {
host := req.Host
if h, _, err := net.SplitHostPort(host); err == nil {
host = h
}
if !strings.HasSuffix(host, domain) {
logger.Printf("Warning: request host is %q but using configured cookie domain of %q", host, domain)
}
}
return &http.Cookie{
Name: name,
Value: value,
Path: path,
Domain: domain,
HttpOnly: httpOnly,
Secure: secure,
Expires: now.Add(expiration),
}
}

View File

@ -4,12 +4,14 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"regexp"
"strings" "strings"
"time" "time"
"github.com/pusher/oauth2_proxy/cookie" "github.com/pusher/oauth2_proxy/cookie"
"github.com/pusher/oauth2_proxy/pkg/apis/options" "github.com/pusher/oauth2_proxy/pkg/apis/options"
"github.com/pusher/oauth2_proxy/pkg/apis/sessions" "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
"github.com/pusher/oauth2_proxy/pkg/cookies"
"github.com/pusher/oauth2_proxy/pkg/sessions/utils" "github.com/pusher/oauth2_proxy/pkg/sessions/utils"
) )
@ -19,10 +21,14 @@ var _ sessions.SessionStore = &SessionStore{}
// SessionStore is an implementation of the sessions.SessionStore // SessionStore is an implementation of the sessions.SessionStore
// interface that stores sessions in client side cookies // interface that stores sessions in client side cookies
type SessionStore struct { type SessionStore struct {
CookieCipher *cookie.Cipher CookieCipher *cookie.Cipher
CookieExpire time.Duration CookieDomain string
CookieName string CookieExpire time.Duration
CookieSecret string CookieHTTPOnly bool
CookieName string
CookiePath string
CookieSecret string
CookieSecure bool
} }
// SaveSession takes a sessions.SessionState and stores the information from it // SaveSession takes a sessions.SessionState and stores the information from it
@ -54,7 +60,35 @@ func (s *SessionStore) LoadSession(req *http.Request) (*sessions.SessionState, e
// ClearSession clears any saved session information by writing a cookie to // ClearSession clears any saved session information by writing a cookie to
// clear the session // clear the session
func (s *SessionStore) ClearSession(rw http.ResponseWriter, req *http.Request) error { func (s *SessionStore) ClearSession(rw http.ResponseWriter, req *http.Request) error {
return fmt.Errorf("method not implemented") var cookies []*http.Cookie
// matches CookieName, CookieName_<number>
var cookieNameRegex = regexp.MustCompile(fmt.Sprintf("^%s(_\\d+)?$", s.CookieName))
for _, c := range req.Cookies() {
if cookieNameRegex.MatchString(c.Name) {
clearCookie := s.makeCookie(req, c.Name, "", time.Hour*-1)
http.SetCookie(rw, clearCookie)
cookies = append(cookies, clearCookie)
}
}
return nil
}
func (s *SessionStore) makeCookie(req *http.Request, name string, value string, expiration time.Duration) *http.Cookie {
return cookies.MakeCookie(
req,
name,
value,
s.CookiePath,
s.CookieDomain,
s.CookieHTTPOnly,
s.CookieSecure,
expiration,
time.Now(),
)
} }
// NewCookieSessionStore initialises a new instance of the SessionStore from // NewCookieSessionStore initialises a new instance of the SessionStore from
@ -70,10 +104,14 @@ func NewCookieSessionStore(opts options.CookieStoreOptions, cookieOpts *options.
} }
return &SessionStore{ return &SessionStore{
CookieCipher: cipher, CookieCipher: cipher,
CookieExpire: cookieOpts.CookieExpire, CookieDomain: cookieOpts.CookieDomain,
CookieName: cookieOpts.CookieName, CookieExpire: cookieOpts.CookieExpire,
CookieSecret: cookieOpts.CookieSecret, CookieHTTPOnly: cookieOpts.CookieHTTPOnly,
CookieName: cookieOpts.CookieName,
CookiePath: cookieOpts.CookiePath,
CookieSecret: cookieOpts.CookieSecret,
CookieSecure: cookieOpts.CookieSecure,
}, nil }, nil
} }

View File

@ -2,6 +2,7 @@ package sessions_test
import ( import (
"net/http" "net/http"
"net/http/httptest"
"testing" "testing"
"time" "time"
@ -9,6 +10,7 @@ import (
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/pusher/oauth2_proxy/pkg/apis/options" "github.com/pusher/oauth2_proxy/pkg/apis/options"
sessionsapi "github.com/pusher/oauth2_proxy/pkg/apis/sessions" sessionsapi "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
"github.com/pusher/oauth2_proxy/pkg/cookies"
"github.com/pusher/oauth2_proxy/pkg/sessions" "github.com/pusher/oauth2_proxy/pkg/sessions"
"github.com/pusher/oauth2_proxy/pkg/sessions/cookie" "github.com/pusher/oauth2_proxy/pkg/sessions/cookie"
) )
@ -23,16 +25,14 @@ var _ = Describe("NewSessionStore", func() {
var cookieOpts *options.CookieOptions var cookieOpts *options.CookieOptions
var request *http.Request var request *http.Request
var response http.ResponseWriter var response *httptest.ResponseRecorder
var session *sessionsapi.SessionState var session *sessionsapi.SessionState
CheckCookieOptions := func() { CheckCookieOptions := func() {
Context("the cookies returned", func() { Context("the cookies returned", func() {
var cookies []*http.Cookie var cookies []*http.Cookie
BeforeEach(func() { BeforeEach(func() {
req := http.Request{} cookies = response.Result().Cookies()
req.Header.Add("Cookie", response.Header().Get("Set-Cookie"))
cookies = req.Cookies()
}) })
It("have the correct name set", func() { It("have the correct name set", func() {
@ -97,6 +97,17 @@ var _ = Describe("NewSessionStore", func() {
Context("when ClearSession is called", func() { Context("when ClearSession is called", func() {
BeforeEach(func() { BeforeEach(func() {
cookie := cookies.MakeCookie(request,
cookieOpts.CookieName,
"foo",
cookieOpts.CookiePath,
cookieOpts.CookieDomain,
cookieOpts.CookieHTTPOnly,
cookieOpts.CookieSecure,
cookieOpts.CookieExpire,
time.Now(),
)
request.AddCookie(cookie)
err := ss.ClearSession(response, request) err := ss.ClearSession(response, request)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
}) })
@ -166,6 +177,9 @@ var _ = Describe("NewSessionStore", func() {
CookieSecure: true, CookieSecure: true,
CookieHTTPOnly: true, CookieHTTPOnly: true,
} }
request = httptest.NewRequest("GET", "http://example.com/", nil)
response = httptest.NewRecorder()
}) })
Context("with type 'cookie'", func() { Context("with type 'cookie'", func() {