authelia/internal/handlers/handler_verify_test.go
James Elliott ddea31193b
feature(oidc): add support for OpenID Connect
OpenID connect has become a standard when it comes to authentication and
in order to fix a security concern around forwarding authentication and authorization information
it has been decided to add support for it.

This feature is in beta version and only enabled when there is a configuration for it.
Before enabling it in production, please consider that it's in beta with potential bugs and that there
are several production critical features still missing such as all OIDC related data is stored in
configuration or memory. This means you are potentially going to experience issues with HA
deployments, or when restarting a single instance specifically related to OIDC.

We are still working on adding the remaining set of features before making it GA as soon as possible.

Related to #189

Co-authored-by: Clement Michaud <clement.michaud34@gmail.com>
2021-05-05 00:15:36 +02:00

1249 lines
42 KiB
Go

package handlers
import (
"fmt"
"net"
"net/url"
"regexp"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/authelia/authelia/internal/authentication"
"github.com/authelia/authelia/internal/authorization"
"github.com/authelia/authelia/internal/configuration/schema"
"github.com/authelia/authelia/internal/mocks"
"github.com/authelia/authelia/internal/session"
"github.com/authelia/authelia/internal/utils"
)
var verifyGetCfg = schema.AuthenticationBackendConfiguration{
RefreshInterval: schema.RefreshIntervalDefault,
LDAP: &schema.LDAPAuthenticationBackendConfiguration{},
}
func TestShouldRaiseWhenTargetUrlIsMalformed(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Ctx.Request.Header.Set("X-Forwarded-Proto", "https")
mock.Ctx.Request.Header.Set("X-Forwarded-Host", "home.example.com")
mock.Ctx.Request.Header.Set("X-Forwarded-URI", "/abc")
originalURL, err := mock.Ctx.GetOriginalURL()
assert.NoError(t, err)
expectedURL, err := url.ParseRequestURI("https://home.example.com/abc")
assert.NoError(t, err)
assert.Equal(t, expectedURL, originalURL)
}
func TestShouldRaiseWhenNoHeaderProvidedToDetectTargetURL(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
_, err := mock.Ctx.GetOriginalURL()
assert.Error(t, err)
assert.Equal(t, "Missing header X-Forwarded-Proto", err.Error())
}
func TestShouldRaiseWhenNoXForwardedHostHeaderProvidedToDetectTargetURL(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Ctx.Request.Header.Set("X-Forwarded-Proto", "https")
_, err := mock.Ctx.GetOriginalURL()
assert.Error(t, err)
assert.Equal(t, "Missing header X-Forwarded-Host", err.Error())
}
func TestShouldRaiseWhenXForwardedProtoIsNotParsable(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Ctx.Request.Header.Set("X-Forwarded-Proto", "!:;;:,")
mock.Ctx.Request.Header.Set("X-Forwarded-Host", "myhost.local")
_, err := mock.Ctx.GetOriginalURL()
assert.Error(t, err)
assert.Equal(t, "Unable to parse URL !:;;:,://myhost.local: parse \"!:;;:,://myhost.local\": invalid URI for request", err.Error())
}
func TestShouldRaiseWhenXForwardedURIIsNotParsable(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Ctx.Request.Header.Set("X-Forwarded-Proto", "https")
mock.Ctx.Request.Header.Set("X-Forwarded-Host", "myhost.local")
mock.Ctx.Request.Header.Set("X-Forwarded-URI", "!:;;:,")
_, err := mock.Ctx.GetOriginalURL()
require.Error(t, err)
assert.Equal(t, "Unable to parse URL https://myhost.local!:;;:,: parse \"https://myhost.local!:;;:,\": invalid port \":,\" after host", err.Error())
}
// Test parseBasicAuth.
func TestShouldRaiseWhenHeaderDoesNotContainBasicPrefix(t *testing.T) {
_, _, err := parseBasicAuth(ProxyAuthorizationHeader, "alzefzlfzemjfej==")
assert.Error(t, err)
assert.Equal(t, "Basic prefix not found in Proxy-Authorization header", err.Error())
}
func TestShouldRaiseWhenCredentialsAreNotInBase64(t *testing.T) {
_, _, err := parseBasicAuth(ProxyAuthorizationHeader, "Basic alzefzlfzemjfej==")
assert.Error(t, err)
assert.Equal(t, "illegal base64 data at input byte 16", err.Error())
}
func TestShouldRaiseWhenCredentialsAreNotInCorrectForm(t *testing.T) {
// The decoded format should be user:password.
_, _, err := parseBasicAuth(ProxyAuthorizationHeader, "Basic am9obiBwYXNzd29yZA==")
assert.Error(t, err)
assert.Equal(t, "Format of Proxy-Authorization header must be user:password", err.Error())
}
func TestShouldUseProvidedHeaderName(t *testing.T) {
// The decoded format should be user:password.
_, _, err := parseBasicAuth("HeaderName", "")
assert.Error(t, err)
assert.Equal(t, "Basic prefix not found in HeaderName header", err.Error())
}
func TestShouldReturnUsernameAndPassword(t *testing.T) {
// the decoded format should be user:password.
user, password, err := parseBasicAuth(ProxyAuthorizationHeader, "Basic am9objpwYXNzd29yZA==")
assert.NoError(t, err)
assert.Equal(t, "john", user)
assert.Equal(t, "password", password)
}
// Test isTargetURLAuthorized.
func TestShouldCheckAuthorizationMatching(t *testing.T) {
type Rule struct {
Policy string
AuthLevel authentication.Level
ExpectedMatching authorizationMatching
}
rules := []Rule{
{"bypass", authentication.NotAuthenticated, Authorized},
{"bypass", authentication.OneFactor, Authorized},
{"bypass", authentication.TwoFactor, Authorized},
{"one_factor", authentication.NotAuthenticated, NotAuthorized},
{"one_factor", authentication.OneFactor, Authorized},
{"one_factor", authentication.TwoFactor, Authorized},
{"two_factor", authentication.NotAuthenticated, NotAuthorized},
{"two_factor", authentication.OneFactor, NotAuthorized},
{"two_factor", authentication.TwoFactor, Authorized},
{"deny", authentication.NotAuthenticated, NotAuthorized},
{"deny", authentication.OneFactor, Forbidden},
{"deny", authentication.TwoFactor, Forbidden},
}
url, _ := url.ParseRequestURI("https://test.example.com")
for _, rule := range rules {
authorizer := authorization.NewAuthorizer(schema.AccessControlConfiguration{
DefaultPolicy: "deny",
Rules: []schema.ACLRule{{
Domains: []string{"test.example.com"},
Policy: rule.Policy,
}},
})
username := ""
if rule.AuthLevel > authentication.NotAuthenticated {
username = testUsername
}
matching := isTargetURLAuthorized(authorizer, *url, username, []string{}, net.ParseIP("127.0.0.1"), []byte("GET"), rule.AuthLevel)
assert.Equal(t, rule.ExpectedMatching, matching, "policy=%s, authLevel=%v, expected=%v, actual=%v",
rule.Policy, rule.AuthLevel, rule.ExpectedMatching, matching)
}
}
// Test verifyBasicAuth.
func TestShouldVerifyWrongCredentials(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(false, nil)
url, _ := url.ParseRequestURI("https://test.example.com")
_, _, _, _, _, err := verifyBasicAuth(ProxyAuthorizationHeader, []byte("Basic am9objpwYXNzd29yZA=="), *url, mock.Ctx)
assert.Error(t, err)
}
type TestCase struct {
URL string
Authorization string
ExpectedStatusCode int
}
func (tc TestCase) String() string {
return fmt.Sprintf("url=%s, auth=%s, exp_status=%d", tc.URL, tc.Authorization, tc.ExpectedStatusCode)
}
type BasicAuthorizationSuite struct {
suite.Suite
}
func NewBasicAuthorizationSuite() *BasicAuthorizationSuite {
return &BasicAuthorizationSuite{}
}
func (s *BasicAuthorizationSuite) TestShouldNotBeAbleToParseBasicAuth() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpaaaaaaaaaaaaaaaa")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldApplyDefaultPolicy() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfBypassDomain() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://bypass.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfOneFactorDomain() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfTwoFactorDomain() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfDenyDomain() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://deny.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 403, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgOk() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.QueryArgs().Add("auth", "basic")
mock.Ctx.Request.Header.Set("Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 200, mock.Ctx.Response.StatusCode())
}
func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingNoHeader() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.QueryArgs().Add("auth", "basic")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
assert.NotEmpty(s.T(), mock.Ctx.Response.Header.Peek("WWW-Authenticate"))
assert.Regexp(s.T(), regexp.MustCompile("^Basic realm="), string(mock.Ctx.Response.Header.Peek("WWW-Authenticate")))
}
func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingEmptyHeader() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.QueryArgs().Add("auth", "basic")
mock.Ctx.Request.Header.Set("Authorization", "")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
assert.NotEmpty(s.T(), mock.Ctx.Response.Header.Peek("WWW-Authenticate"))
assert.Regexp(s.T(), regexp.MustCompile("^Basic realm="), string(mock.Ctx.Response.Header.Peek("WWW-Authenticate")))
}
func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingWrongPassword() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.QueryArgs().Add("auth", "basic")
mock.Ctx.Request.Header.Set("Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(false, nil)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
assert.NotEmpty(s.T(), mock.Ctx.Response.Header.Peek("WWW-Authenticate"))
assert.Regexp(s.T(), regexp.MustCompile("^Basic realm="), string(mock.Ctx.Response.Header.Peek("WWW-Authenticate")))
}
func (s *BasicAuthorizationSuite) TestShouldVerifyAuthBasicArgFailingWrongHeader() {
mock := mocks.NewMockAutheliaCtx(s.T())
defer mock.Close()
mock.Ctx.QueryArgs().Add("auth", "basic")
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(s.T(), 401, mock.Ctx.Response.StatusCode())
assert.Equal(s.T(), "Unauthorized", string(mock.Ctx.Response.Body()))
assert.NotEmpty(s.T(), mock.Ctx.Response.Header.Peek("WWW-Authenticate"))
assert.Regexp(s.T(), regexp.MustCompile("^Basic realm="), string(mock.Ctx.Response.Header.Peek("WWW-Authenticate")))
}
func TestShouldVerifyAuthorizationsUsingBasicAuth(t *testing.T) {
suite.Run(t, NewBasicAuthorizationSuite())
}
func TestShouldVerifyWrongCredentialsInBasicAuth(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("wrongpass")).
Return(false, nil)
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objp3cm9uZ3Bhc3M=")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://test.example.com", actualStatus, expStatus)
}
func TestShouldVerifyFailingPasswordCheckingInBasicAuth(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("wrongpass")).
Return(false, fmt.Errorf("Failed"))
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objp3cm9uZ3Bhc3M=")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://test.example.com", actualStatus, expStatus)
}
func TestShouldVerifyFailingDetailsFetchingInBasicAuth(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.UserProviderMock.EXPECT().
CheckUserPassword(gomock.Eq("john"), gomock.Eq("password")).
Return(true, nil)
mock.UserProviderMock.EXPECT().
GetDetails(gomock.Eq("john")).
Return(nil, fmt.Errorf("Failed"))
mock.Ctx.Request.Header.Set("Proxy-Authorization", "Basic am9objpwYXNzd29yZA==")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://test.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
expStatus, actualStatus := 401, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://test.example.com", actualStatus, expStatus)
}
func TestShouldNotCrashOnEmptyEmail(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.Emails = nil
userSession.AuthenticationLevel = authentication.OneFactor
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
fmt.Printf("Time is %v\n", userSession.RefreshTTL)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://bypass.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
expStatus, actualStatus := 200, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> StatusCode=%d != ExpectedStatusCode=%d",
"https://bypass.example.com", actualStatus, expStatus)
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek("Remote-Email"))
}
type Pair struct {
URL string
Username string
Emails []string
AuthenticationLevel authentication.Level
ExpectedStatusCode int
}
func (p Pair) String() string {
return fmt.Sprintf("url=%s, username=%s, auth_lvl=%d, exp_status=%d",
p.URL, p.Username, p.AuthenticationLevel, p.ExpectedStatusCode)
}
func TestShouldVerifyAuthorizationsUsingSessionCookie(t *testing.T) {
testCases := []Pair{
{"https://test.example.com", "", nil, authentication.NotAuthenticated, 401},
{"https://bypass.example.com", "", nil, authentication.NotAuthenticated, 200},
{"https://one-factor.example.com", "", nil, authentication.NotAuthenticated, 401},
{"https://two-factor.example.com", "", nil, authentication.NotAuthenticated, 401},
{"https://deny.example.com", "", nil, authentication.NotAuthenticated, 401},
{"https://test.example.com", "john", []string{"john.doe@example.com"}, authentication.OneFactor, 403},
{"https://bypass.example.com", "john", []string{"john.doe@example.com"}, authentication.OneFactor, 200},
{"https://one-factor.example.com", "john", []string{"john.doe@example.com"}, authentication.OneFactor, 200},
{"https://two-factor.example.com", "john", []string{"john.doe@example.com"}, authentication.OneFactor, 401},
{"https://deny.example.com", "john", []string{"john.doe@example.com"}, authentication.OneFactor, 403},
{"https://test.example.com", "john", []string{"john.doe@example.com"}, authentication.TwoFactor, 403},
{"https://bypass.example.com", "john", []string{"john.doe@example.com"}, authentication.TwoFactor, 200},
{"https://one-factor.example.com", "john", []string{"john.doe@example.com"}, authentication.TwoFactor, 200},
{"https://two-factor.example.com", "john", []string{"john.doe@example.com"}, authentication.TwoFactor, 200},
{"https://deny.example.com", "john", []string{"john.doe@example.com"}, authentication.TwoFactor, 403},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.String(), func(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = testCase.Username
userSession.Emails = testCase.Emails
userSession.AuthenticationLevel = testCase.AuthenticationLevel
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", testCase.URL)
VerifyGet(verifyGetCfg)(mock.Ctx)
expStatus, actualStatus := testCase.ExpectedStatusCode, mock.Ctx.Response.StatusCode()
assert.Equal(t, expStatus, actualStatus, "URL=%s -> AuthLevel=%d, StatusCode=%d != ExpectedStatusCode=%d",
testCase.URL, testCase.AuthenticationLevel, actualStatus, expStatus)
if testCase.ExpectedStatusCode == 200 && testCase.Username != "" {
assert.Equal(t, []byte(testCase.Username), mock.Ctx.Response.Header.Peek("Remote-User"))
assert.Equal(t, []byte("john.doe@example.com"), mock.Ctx.Response.Header.Peek("Remote-Email"))
} else {
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek("Remote-User"))
assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek("Remote-Email"))
}
})
}
}
func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
clock := mocks.TestingClock{}
clock.Set(time.Now())
past := clock.Now().Add(-1 * time.Hour)
mock.Ctx.Configuration.Session.Inactivity = testInactivity
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = past.Unix()
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
// The session has been destroyed.
newUserSession := mock.Ctx.GetSession()
assert.Equal(t, "", newUserSession.Username)
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
// Check the inactivity timestamp has been updated to current time in the new session.
assert.Equal(t, clock.Now().Unix(), newUserSession.LastActivity)
}
func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
clock := mocks.TestingClock{}
clock.Set(time.Now())
mock.Ctx.Configuration.Session.Inactivity = "10s"
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = clock.Now().Add(-1 * time.Hour).Unix()
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
// The session has been destroyed.
newUserSession := mock.Ctx.GetSession()
assert.Equal(t, "", newUserSession.Username)
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
}
func TestShouldKeepSessionWhenUserCheckedRememberMeAndIsInactiveForTooLong(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
mock.Ctx.Configuration.Session.Inactivity = testInactivity
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.Emails = []string{"john.doe@example.com"}
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = 0
userSession.KeepMeLoggedIn = true
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
// Check the session is still active.
newUserSession := mock.Ctx.GetSession()
assert.Equal(t, "john", newUserSession.Username)
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
// Check the inactivity timestamp is set to 0 in case remember me is checked.
assert.Equal(t, int64(0), newUserSession.LastActivity)
}
func TestShouldKeepSessionWhenInactivityTimeoutHasNotBeenExceeded(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
mock.Ctx.Configuration.Session.Inactivity = testInactivity
past := mock.Clock.Now().Add(-1 * time.Hour)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.Emails = []string{"john.doe@example.com"}
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = past.Unix()
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
// The session has been destroyed.
newUserSession := mock.Ctx.GetSession()
assert.Equal(t, "john", newUserSession.Username)
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
// Check the inactivity timestamp has been updated to current time in the new session.
assert.Equal(t, mock.Clock.Now().Unix(), newUserSession.LastActivity)
}
// In the case of Traefik and Nginx ingress controller in Kube, the response to an inactive
// session is 302 instead of 401.
func TestShouldRedirectWhenSessionInactiveForTooLongAndRDParamProvided(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
clock := mocks.TestingClock{}
clock.Set(time.Now())
mock.Ctx.Configuration.Session.Inactivity = testInactivity
// Reload the session provider since the configuration is indirect.
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil)
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
past := clock.Now().Add(-1 * time.Hour)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = past.Unix()
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.QueryArgs().Add("rd", "https://login.example.com")
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
mock.Ctx.Request.Header.Set("X-Forwarded-Method", "GET")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, "Found. Redirecting to https://login.example.com?rd=https%3A%2F%2Ftwo-factor.example.com&rm=GET",
string(mock.Ctx.Response.Body()))
assert.Equal(t, 302, mock.Ctx.Response.StatusCode())
// Check the inactivity timestamp has been updated to current time in the new session.
newUserSession := mock.Ctx.GetSession()
assert.Equal(t, clock.Now().Unix(), newUserSession.LastActivity)
}
func TestShouldUpdateInactivityTimestampEvenWhenHittingForbiddenResources(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
mock.Ctx.Configuration.Session.Inactivity = testInactivity
past := mock.Clock.Now().Add(-1 * time.Hour)
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = past.Unix()
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://deny.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
// The resource if forbidden.
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
// Check the inactivity timestamp has been updated to current time in the new session.
newUserSession := mock.Ctx.GetSession()
assert.Equal(t, mock.Clock.Now().Unix(), newUserSession.LastActivity)
}
func TestShouldURLEncodeRedirectionURLParameter(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.NotAuthenticated
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
mock.Ctx.Request.SetHost("mydomain.com")
mock.Ctx.Request.SetRequestURI("/?rd=https://auth.mydomain.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, "Found. Redirecting to https://auth.mydomain.com?rd=https%3A%2F%2Ftwo-factor.example.com",
string(mock.Ctx.Response.Body()))
}
func TestIsDomainProtected(t *testing.T) {
GetURL := func(u string) *url.URL {
x, err := url.ParseRequestURI(u)
require.NoError(t, err)
return x
}
assert.True(t, isURLUnderProtectedDomain(
GetURL("http://mytest.example.com/abc/?query=abc"), "example.com"))
assert.True(t, isURLUnderProtectedDomain(
GetURL("http://example.com/abc/?query=abc"), "example.com"))
assert.True(t, isURLUnderProtectedDomain(
GetURL("https://mytest.example.com/abc/?query=abc"), "example.com"))
// Cookies readable by a service on a machine is also readable by a service on the same machine
// with a different port as mentioned in https://tools.ietf.org/html/rfc6265#section-8.5.
assert.True(t, isURLUnderProtectedDomain(
GetURL("https://mytest.example.com:8080/abc/?query=abc"), "example.com"))
}
func TestSchemeIsHTTPS(t *testing.T) {
GetURL := func(u string) *url.URL {
x, err := url.ParseRequestURI(u)
require.NoError(t, err)
return x
}
assert.False(t, isSchemeHTTPS(
GetURL("http://mytest.example.com/abc/?query=abc")))
assert.False(t, isSchemeHTTPS(
GetURL("ws://mytest.example.com/abc/?query=abc")))
assert.False(t, isSchemeHTTPS(
GetURL("wss://mytest.example.com/abc/?query=abc")))
assert.True(t, isSchemeHTTPS(
GetURL("https://mytest.example.com/abc/?query=abc")))
}
func TestSchemeIsWSS(t *testing.T) {
GetURL := func(u string) *url.URL {
x, err := url.ParseRequestURI(u)
require.NoError(t, err)
return x
}
assert.False(t, isSchemeWSS(
GetURL("ws://mytest.example.com/abc/?query=abc")))
assert.False(t, isSchemeWSS(
GetURL("http://mytest.example.com/abc/?query=abc")))
assert.False(t, isSchemeWSS(
GetURL("https://mytest.example.com/abc/?query=abc")))
assert.True(t, isSchemeWSS(
GetURL("wss://mytest.example.com/abc/?query=abc")))
}
func TestShouldNotRefreshUserGroupsFromBackend(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
// Setup pointer to john so we can adjust it during the test.
user := &authentication.UserDetails{
Username: "john",
Groups: []string{
"admin",
"users",
},
Emails: []string{
"john@example.com",
},
}
cfg := verifyGetCfg
cfg.RefreshInterval = "disable"
verifyGet := VerifyGet(cfg)
mock.UserProviderMock.EXPECT().GetDetails("john").Times(0)
clock := mocks.TestingClock{}
clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = user.Username
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = clock.Now().Unix()
userSession.Groups = user.Groups
userSession.Emails = user.Emails
userSession.KeepMeLoggedIn = true
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
mock.Ctx.Request.Header.Set("X-Original-URL", "https://admin.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Check Refresh TTL has not been updated.
userSession = mock.Ctx.GetSession()
// Check user groups are correct.
require.Len(t, userSession.Groups, len(user.Groups))
assert.Equal(t, utils.RFC3339Zero, userSession.RefreshTTL.Unix())
assert.Equal(t, "admin", userSession.Groups[0])
assert.Equal(t, "users", userSession.Groups[1])
mock.Ctx.Request.Header.Set("X-Original-URL", "https://admin.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Check admin group is not removed from the session.
userSession = mock.Ctx.GetSession()
assert.Equal(t, utils.RFC3339Zero, userSession.RefreshTTL.Unix())
require.Len(t, userSession.Groups, 2)
assert.Equal(t, "admin", userSession.Groups[0])
assert.Equal(t, "users", userSession.Groups[1])
}
func TestShouldNotRefreshUserGroupsFromBackendWhenDisabled(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
// Setup user john.
user := &authentication.UserDetails{
Username: "john",
Groups: []string{
"admin",
"users",
},
Emails: []string{
"john@example.com",
},
}
mock.UserProviderMock.EXPECT().GetDetails("john").Times(0)
clock := mocks.TestingClock{}
clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = user.Username
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = clock.Now().Unix()
userSession.RefreshTTL = clock.Now().Add(-1 * time.Minute)
userSession.Groups = user.Groups
userSession.Emails = user.Emails
userSession.KeepMeLoggedIn = true
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
config := verifyGetCfg
config.RefreshInterval = schema.ProfileRefreshDisabled
VerifyGet(config)(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Session time should NOT have been updated, it should still have a refresh TTL 1 minute in the past.
userSession = mock.Ctx.GetSession()
assert.Equal(t, clock.Now().Add(-1*time.Minute).Unix(), userSession.RefreshTTL.Unix())
}
func TestShouldDestroySessionWhenUserNotExist(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
// Setup user john.
user := &authentication.UserDetails{
Username: "john",
Groups: []string{
"admin",
"users",
},
Emails: []string{
"john@example.com",
},
}
mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(1)
clock := mocks.TestingClock{}
clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = user.Username
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = clock.Now().Unix()
userSession.RefreshTTL = clock.Now().Add(-1 * time.Minute)
userSession.Groups = user.Groups
userSession.Emails = user.Emails
userSession.KeepMeLoggedIn = true
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Session time should NOT have been updated, it should still have a refresh TTL 1 minute in the past.
userSession = mock.Ctx.GetSession()
assert.Equal(t, clock.Now().Add(5*time.Minute).Unix(), userSession.RefreshTTL.Unix())
// Simulate a Deleted User
userSession.RefreshTTL = clock.Now().Add(-1 * time.Minute)
err = mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.UserProviderMock.EXPECT().GetDetails("john").Return(nil, authentication.ErrUserNotFound).Times(1)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, 401, mock.Ctx.Response.StatusCode())
userSession = mock.Ctx.GetSession()
assert.Equal(t, "", userSession.Username)
assert.Equal(t, authentication.NotAuthenticated, userSession.AuthenticationLevel)
}
func TestShouldGetRemovedUserGroupsFromBackend(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
// Setup pointer to john so we can adjust it during the test.
user := &authentication.UserDetails{
Username: "john",
Groups: []string{
"admin",
"users",
},
Emails: []string{
"john@example.com",
},
}
verifyGet := VerifyGet(verifyGetCfg)
mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(2)
clock := mocks.TestingClock{}
clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = user.Username
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = clock.Now().Unix()
userSession.RefreshTTL = clock.Now().Add(-1 * time.Minute)
userSession.Groups = user.Groups
userSession.Emails = user.Emails
userSession.KeepMeLoggedIn = true
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Request should get refresh settings and new user details.
mock.Ctx.Request.Header.Set("X-Original-URL", "https://admin.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Check Refresh TTL has been updated since admin.example.com has a group subject and refresh is enabled.
userSession = mock.Ctx.GetSession()
// Check user groups are correct.
require.Len(t, userSession.Groups, len(user.Groups))
assert.Equal(t, clock.Now().Add(5*time.Minute).Unix(), userSession.RefreshTTL.Unix())
assert.Equal(t, "admin", userSession.Groups[0])
assert.Equal(t, "users", userSession.Groups[1])
// Remove the admin group, and force the next request to refresh.
user.Groups = []string{"users"}
userSession.RefreshTTL = clock.Now().Add(-1 * time.Second)
err = mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://admin.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
// Check admin group is removed from the session.
userSession = mock.Ctx.GetSession()
assert.Equal(t, clock.Now().Add(5*time.Minute).Unix(), userSession.RefreshTTL.Unix())
require.Len(t, userSession.Groups, 1)
assert.Equal(t, "users", userSession.Groups[0])
}
func TestShouldGetAddedUserGroupsFromBackend(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
// Setup pointer to john so we can adjust it during the test.
user := &authentication.UserDetails{
Username: "john",
Groups: []string{
"admin",
"users",
},
Emails: []string{
"john@example.com",
},
}
mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(1)
verifyGet := VerifyGet(verifyGetCfg)
mock.Clock.Set(time.Now())
userSession := mock.Ctx.GetSession()
userSession.Username = user.Username
userSession.AuthenticationLevel = authentication.TwoFactor
userSession.LastActivity = mock.Clock.Now().Unix()
userSession.RefreshTTL = mock.Clock.Now().Add(-1 * time.Minute)
userSession.Groups = user.Groups
userSession.Emails = user.Emails
userSession.KeepMeLoggedIn = true
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
mock.Ctx.Request.Header.Set("X-Original-URL", "https://grafana.example.com")
verifyGet(mock.Ctx)
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
// Check Refresh TTL has been updated since grafana.example.com has a group subject and refresh is enabled.
userSession = mock.Ctx.GetSession()
// Check user groups are correct.
require.Len(t, userSession.Groups, len(user.Groups))
assert.Equal(t, mock.Clock.Now().Add(5*time.Minute).Unix(), userSession.RefreshTTL.Unix())
assert.Equal(t, "admin", userSession.Groups[0])
assert.Equal(t, "users", userSession.Groups[1])
// Add the grafana group, and force the next request to refresh.
user.Groups = append(user.Groups, "grafana")
userSession.RefreshTTL = mock.Clock.Now().Add(-1 * time.Second)
err = mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
// Reset otherwise we get the last 403 when we check the Response. Is there a better way to do this?
mock.Close()
mock = mocks.NewMockAutheliaCtx(t)
defer mock.Close()
err = mock.Ctx.SaveSession(userSession)
assert.NoError(t, err)
mock.Clock.Set(time.Now())
gomock.InOrder(
mock.UserProviderMock.EXPECT().GetDetails("john").Return(user, nil).Times(1),
)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://grafana.example.com")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
// Check admin group is removed from the session.
userSession = mock.Ctx.GetSession()
assert.Equal(t, true, userSession.KeepMeLoggedIn)
assert.Equal(t, authentication.TwoFactor, userSession.AuthenticationLevel)
assert.Equal(t, mock.Clock.Now().Add(5*time.Minute).Unix(), userSession.RefreshTTL.Unix())
require.Len(t, userSession.Groups, 3)
assert.Equal(t, "admin", userSession.Groups[0])
assert.Equal(t, "users", userSession.Groups[1])
assert.Equal(t, "grafana", userSession.Groups[2])
}
func TestShouldCheckValidSessionUsernameHeaderAndReturn200(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
expectedStatusCode := 200
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.OneFactor
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
mock.Ctx.Request.Header.Set(SessionUsernameHeader, testUsername)
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, expectedStatusCode, mock.Ctx.Response.StatusCode())
assert.Equal(t, "", string(mock.Ctx.Response.Body()))
}
func TestShouldCheckInvalidSessionUsernameHeaderAndReturn401(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Clock.Set(time.Now())
expectedStatusCode := 401
userSession := mock.Ctx.GetSession()
userSession.Username = testUsername
userSession.AuthenticationLevel = authentication.OneFactor
userSession.RefreshTTL = mock.Clock.Now().Add(5 * time.Minute)
err := mock.Ctx.SaveSession(userSession)
require.NoError(t, err)
mock.Ctx.Request.Header.Set("X-Original-URL", "https://one-factor.example.com")
mock.Ctx.Request.Header.Set(SessionUsernameHeader, "root")
VerifyGet(verifyGetCfg)(mock.Ctx)
assert.Equal(t, expectedStatusCode, mock.Ctx.Response.StatusCode())
assert.Equal(t, "Unauthorized", string(mock.Ctx.Response.Body()))
}
func TestGetProfileRefreshSettings(t *testing.T) {
cfg := verifyGetCfg
refresh, interval := getProfileRefreshSettings(cfg)
assert.Equal(t, true, refresh)
assert.Equal(t, 5*time.Minute, interval)
cfg.RefreshInterval = schema.ProfileRefreshDisabled
refresh, interval = getProfileRefreshSettings(cfg)
assert.Equal(t, false, refresh)
assert.Equal(t, time.Duration(0), interval)
cfg.RefreshInterval = schema.ProfileRefreshAlways
refresh, interval = getProfileRefreshSettings(cfg)
assert.Equal(t, true, refresh)
assert.Equal(t, time.Duration(0), interval)
}