diff --git a/oauthproxy.go b/oauthproxy.go index 5a21072f..d45e6384 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -778,13 +778,17 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request, signOutA p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) return } - session, err := p.getAuthenticatedSession(rw, req) - if err != nil { - logger.Errorf("Error clearing all sessions cookie: %v", err) - } err = p.ClearSessionCookie(rw, req) if signOutAllSessions { - err = p.ClearAllSessions(req, session) + session, errAuthSession := p.getAuthenticatedSession(rw, req) + if errAuthSession != nil { + logger.Errorf("Error clearing all sessions cookie: %v", errAuthSession) + } else { + clearAllError := p.ClearAllSessions(req, session) + if clearAllError != nil { + logger.Errorf("Error clearing session cookie: %v", clearAllError) + } + } } if err != nil { logger.Errorf("Error clearing session cookie: %v", err) diff --git a/pkg/encryption/utils_test.go b/pkg/encryption/utils_test.go index 9e69df84..097fad55 100644 --- a/pkg/encryption/utils_test.go +++ b/pkg/encryption/utils_test.go @@ -87,6 +87,14 @@ func TestSecretBytesNonBase64(t *testing.T) { assert.Equal(t, 32, len(sb32)) } +func TestEncryptStringWithSecret(t *testing.T) { + secret := "my-secret" + input := "my-input" + + result := EncryptStringWithSecret(input, secret) + assert.Equal(t, result, "7DlH3g1Io9AmyD8tVPEPHdpH9N4jsO07mNCkfNcfW2A") +} + func TestSignAndValidate(t *testing.T) { seed := "0123456789abcdef" key := "cookie-name" diff --git a/pkg/middleware/stored_session_test.go b/pkg/middleware/stored_session_test.go index 2d3a6f66..3502b83d 100644 --- a/pkg/middleware/stored_session_test.go +++ b/pkg/middleware/stored_session_test.go @@ -763,9 +763,10 @@ var _ = Describe("Stored Session Suite", func() { }) type fakeSessionStore struct { - SaveFunc func(http.ResponseWriter, *http.Request, *sessionsapi.SessionState) error - LoadFunc func(req *http.Request) (*sessionsapi.SessionState, error) - ClearFunc func(rw http.ResponseWriter, req *http.Request) error + SaveFunc func(http.ResponseWriter, *http.Request, *sessionsapi.SessionState) error + LoadFunc func(req *http.Request) (*sessionsapi.SessionState, error) + ClearFunc func(rw http.ResponseWriter, req *http.Request) error + ClearAllUserSessionsFunc func(req *http.Request, session *sessionsapi.SessionState) error } func (f *fakeSessionStore) Save(rw http.ResponseWriter, req *http.Request, s *sessionsapi.SessionState) error { @@ -788,6 +789,13 @@ func (f *fakeSessionStore) Clear(rw http.ResponseWriter, req *http.Request) erro return nil } +func (f *fakeSessionStore) ClearAllUserSessions(req *http.Request, session *sessionsapi.SessionState) error { + if f.ClearAllUserSessionsFunc != nil { + return f.ClearAllUserSessionsFunc(req, session) + } + return nil +} + func (f *fakeSessionStore) VerifyConnection(_ context.Context) error { return nil } diff --git a/pkg/sessions/persistence/ticket_test.go b/pkg/sessions/persistence/ticket_test.go index 4cc62e2c..c5c6244c 100644 --- a/pkg/sessions/persistence/ticket_test.go +++ b/pkg/sessions/persistence/ticket_test.go @@ -70,15 +70,26 @@ var _ = Describe("Session Ticket Tests", func() { ss := &sessions.SessionState{User: "foobar"} store := map[string][]byte{} - err = t.saveSession(ss, func(k string, v []byte, e time.Duration) error { - store[k] = v - return nil - }) + storeUserSessionList := map[string][]string{} + storedUserSessionListExpected := map[string][]string{ + "16-axDAZ63SxeHvCLMjoF5EEX0ipSzNNqxpUITxPxgk": {t.id}, + } + err = t.saveSession( + ss, + func(k string, v []byte, e time.Duration, s *sessions.SessionState) error { + store[k] = v + return nil + }, + func(key string, value string, d time.Duration) error { + storeUserSessionList[key] = append(storeUserSessionList[key], value) + return nil + }) Expect(err).ToNot(HaveOccurred()) stored, err := sessions.DecodeSessionState(store[t.id], c, false) Expect(err).ToNot(HaveOccurred()) Expect(stored).To(Equal(ss)) + Expect(storeUserSessionList).To(Equal(storedUserSessionListExpected)) }) It("errors when the saveFunc errors", func() { @@ -87,11 +98,29 @@ var _ = Describe("Session Ticket Tests", func() { err = t.saveSession( &sessions.SessionState{User: "foobar"}, - func(k string, v []byte, e time.Duration) error { + func(k string, v []byte, e time.Duration, s *sessions.SessionState) error { return errors.New("save error") + }, + func(key string, value string, d time.Duration) error { + return nil }) Expect(err).To(MatchError(errors.New("save error"))) }) + + It("should not return error when the saverUserMapSession errors", func() { + t, err := newTicket(&options.Cookie{Name: "dummy"}) + Expect(err).ToNot(HaveOccurred()) + + err = t.saveSession( + &sessions.SessionState{User: "foobar"}, + func(k string, v []byte, e time.Duration, s *sessions.SessionState) error { + return nil + }, + func(key string, value string, d time.Duration) error { + return errors.New("save user session error") + }) + Expect(err).To(BeNil()) + }) }) Context("loadSession", func() { diff --git a/pkg/sessions/tests/mock_store.go b/pkg/sessions/tests/mock_store.go index c82d8c08..4879042b 100644 --- a/pkg/sessions/tests/mock_store.go +++ b/pkg/sessions/tests/mock_store.go @@ -14,14 +14,50 @@ type entry struct { expiration time.Duration } +type entryList struct { + data []string + expiration time.Duration +} + // MockStore is a generic in-memory implementation of persistence.Store // for mocking in tests type MockStore struct { cache map[string]entry + cacheList map[string]entryList lockCache map[string]*MockLock elapsed time.Duration } +// LoadList implements persistence.Store. +func (s *MockStore) LoadList(ctx context.Context, key string) ([]string, error) { + entry, ok := s.cacheList[key] + if !ok || entry.expiration <= s.elapsed { + delete(s.cache, key) + return nil, fmt.Errorf("key not found: %s", key) + } + return entry.data, nil +} + +// RPush implements persistence.Store. +func (s *MockStore) RPush(ctx context.Context, key string, value string, time time.Duration) error { + entry, ok := s.cacheList[key] + if ok { + // If the key exists, check if the expiration is still valid + entry.data = append(entry.data, value) + s.cacheList[key] = entry + } else { + // If the key does not exist, create a new entryList + if s.cacheList == nil { + s.cacheList = make(map[string]entryList) + } + s.cacheList[key] = entryList{ + data: []string{value}, + expiration: time, + } + } + return nil +} + // NewMockStore creates a MockStore func NewMockStore() *MockStore { return &MockStore{ diff --git a/pkg/sessions/tests/session_store_tests.go b/pkg/sessions/tests/session_store_tests.go index 8e2d02f9..7320a3a2 100644 --- a/pkg/sessions/tests/session_store_tests.go +++ b/pkg/sessions/tests/session_store_tests.go @@ -451,6 +451,28 @@ func SessionStoreInterfaceTests(in *testInput) { CheckCookieOptions(in) }) + Context("Clear all user sessions", func() { + BeforeEach(func() { + req := httptest.NewRequest("GET", "http://example.com/", nil) + resp := httptest.NewRecorder() + err := in.ss().Save(resp, req, in.session) + Expect(err).ToNot(HaveOccurred()) + resultCookies := resp.Result().Cookies() + for _, c := range resultCookies { + in.request.AddCookie(c) + } + }) + It("should clear all user sessions", func() { + err := in.ss().ClearAllUserSessions(in.request, in.session) + Expect(err).ToNot(HaveOccurred()) + + // Verify that the session is cleared + loadedSession, loadErr := in.ss().Load(in.request) + Expect(loadedSession).To(BeNil()) + Expect(loadErr).To(HaveOccurred()) + }) + }) + Context("when Load is called", func() { Context("with a valid session cookie in the request", func() { BeforeEach(func() {