feat: envoy support (#3793)
Adds support for Envoy and Istio using the X-Authelia-URL header. The documentation will be published just before the release. Co-authored-by: Amir Zarrinkafsh <nightah@me.com>pull/4112/head
parent
18a2bde62e
commit
ed7092c59a
|
@ -204,7 +204,7 @@ func cmdGitHubIssueTemplatesBugReportRunE(cmd *cobra.Command, args []string) (er
|
||||||
data := &tmplIssueTemplateData{
|
data := &tmplIssueTemplateData{
|
||||||
Labels: []string{labelTypeBugUnconfirmed.String(), labelStatusNeedsTriage.String(), labelPriorityNormal.String()},
|
Labels: []string{labelTypeBugUnconfirmed.String(), labelStatusNeedsTriage.String(), labelPriorityNormal.String()},
|
||||||
Versions: tagsRecent,
|
Versions: tagsRecent,
|
||||||
Proxies: []string{"Caddy", "Traefik", "Envoy", "NGINX", "SWAG", "NGINX Proxy Manager", "HAProxy"},
|
Proxies: []string{"Caddy", "Traefik", "Envoy", "Istio", "NGINX", "SWAG", "NGINX Proxy Manager", "HAProxy"},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = tmplGitHubIssueTemplateBug.Execute(f, data); err != nil {
|
if err = tmplGitHubIssueTemplateBug.Execute(f, data); err != nil {
|
||||||
|
|
|
@ -443,7 +443,7 @@ password_policy:
|
||||||
## to anyone. Otherwise restrictions follow the rules defined.
|
## to anyone. Otherwise restrictions follow the rules defined.
|
||||||
##
|
##
|
||||||
## Note: One can use the wildcard * to match any subdomain.
|
## Note: One can use the wildcard * to match any subdomain.
|
||||||
## It must stand at the beginning of the pattern. (example: *.mydomain.com)
|
## It must stand at the beginning of the pattern. (example: *.example.com)
|
||||||
##
|
##
|
||||||
## Note: You must put patterns containing wildcards between simple quotes for the YAML to be syntactically correct.
|
## Note: You must put patterns containing wildcards between simple quotes for the YAML to be syntactically correct.
|
||||||
##
|
##
|
||||||
|
|
|
@ -443,7 +443,7 @@ password_policy:
|
||||||
## to anyone. Otherwise restrictions follow the rules defined.
|
## to anyone. Otherwise restrictions follow the rules defined.
|
||||||
##
|
##
|
||||||
## Note: One can use the wildcard * to match any subdomain.
|
## Note: One can use the wildcard * to match any subdomain.
|
||||||
## It must stand at the beginning of the pattern. (example: *.mydomain.com)
|
## It must stand at the beginning of the pattern. (example: *.example.com)
|
||||||
##
|
##
|
||||||
## Note: You must put patterns containing wildcards between simple quotes for the YAML to be syntactically correct.
|
## Note: You must put patterns containing wildcards between simple quotes for the YAML to be syntactically correct.
|
||||||
##
|
##
|
||||||
|
|
|
@ -614,14 +614,14 @@ func (s *SecondFactorDuoPostSuite) TestShouldRedirectUserToSafeTargetURL() {
|
||||||
duoMock.EXPECT().AuthCall(s.mock.Ctx, gomock.Any()).Return(&response, nil)
|
duoMock.EXPECT().AuthCall(s.mock.Ctx, gomock.Any()).Return(&response, nil)
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(signDuoRequestBody{
|
bodyBytes, err := json.Marshal(signDuoRequestBody{
|
||||||
TargetURL: "https://example.com",
|
TargetURL: "https://mydomain.example.com",
|
||||||
})
|
})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||||
|
|
||||||
DuoPOST(duoMock)(s.mock.Ctx)
|
DuoPOST(duoMock)(s.mock.Ctx)
|
||||||
s.mock.Assert200OK(s.T(), redirectResponse{
|
s.mock.Assert200OK(s.T(), redirectResponse{
|
||||||
Redirect: "https://example.com",
|
Redirect: "https://mydomain.example.com",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,7 +663,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldNotRedirectToUnsafeURL() {
|
||||||
duoMock.EXPECT().AuthCall(s.mock.Ctx, gomock.Any()).Return(&response, nil)
|
duoMock.EXPECT().AuthCall(s.mock.Ctx, gomock.Any()).Return(&response, nil)
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(signDuoRequestBody{
|
bodyBytes, err := json.Marshal(signDuoRequestBody{
|
||||||
TargetURL: "http://example.com",
|
TargetURL: "http://mydomain.example.com",
|
||||||
})
|
})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||||
|
@ -710,7 +710,7 @@ func (s *SecondFactorDuoPostSuite) TestShouldRegenerateSessionForPreventingSessi
|
||||||
duoMock.EXPECT().AuthCall(s.mock.Ctx, gomock.Any()).Return(&response, nil)
|
duoMock.EXPECT().AuthCall(s.mock.Ctx, gomock.Any()).Return(&response, nil)
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(signDuoRequestBody{
|
bodyBytes, err := json.Marshal(signDuoRequestBody{
|
||||||
TargetURL: "http://example.com",
|
TargetURL: "http://mydomain.example.com",
|
||||||
})
|
})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.mock.Ctx.Request.SetBody(bodyBytes)
|
s.mock.Ctx.Request.SetBody(bodyBytes)
|
||||||
|
|
|
@ -167,7 +167,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(signTOTPRequestBody{
|
bodyBytes, err := json.Marshal(signTOTPRequestBody{
|
||||||
Token: "abc",
|
Token: "abc",
|
||||||
TargetURL: "https://example.com",
|
TargetURL: "https://mydomain.example.com",
|
||||||
})
|
})
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
@ -175,7 +175,7 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {
|
||||||
|
|
||||||
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
TimeBasedOneTimePasswordPOST(s.mock.Ctx)
|
||||||
s.mock.Assert200OK(s.T(), redirectResponse{
|
s.mock.Assert200OK(s.T(), redirectResponse{
|
||||||
Redirect: "https://example.com",
|
Redirect: "https://mydomain.example.com",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ func (s *HandlerSignTOTPSuite) TestShouldNotRedirectToUnsafeURL() {
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(signTOTPRequestBody{
|
bodyBytes, err := json.Marshal(signTOTPRequestBody{
|
||||||
Token: "abc",
|
Token: "abc",
|
||||||
TargetURL: "http://example.com",
|
TargetURL: "http://mydomain.example.com",
|
||||||
})
|
})
|
||||||
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
|
@ -180,7 +180,6 @@ func verifySessionCookie(ctx *middlewares.AutheliaCtx, targetURL *url.URL, userS
|
||||||
func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, isBasicAuth bool, username string, method []byte) {
|
func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, isBasicAuth bool, username string, method []byte) {
|
||||||
var (
|
var (
|
||||||
statusCode int
|
statusCode int
|
||||||
redirectionURL string
|
|
||||||
friendlyUsername string
|
friendlyUsername string
|
||||||
friendlyRequestMethod string
|
friendlyRequestMethod string
|
||||||
)
|
)
|
||||||
|
@ -200,10 +199,6 @@ func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, is
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kubernetes ingress controller and Traefik use the rd parameter of the verify
|
|
||||||
// endpoint to provide the URL of the login portal. The target URL of the user
|
|
||||||
// is computed from X-Forwarded-* headers or X-Original-URL.
|
|
||||||
rd := string(ctx.QueryArgs().Peek("rd"))
|
|
||||||
rm := string(method)
|
rm := string(method)
|
||||||
|
|
||||||
switch rm {
|
switch rm {
|
||||||
|
@ -213,17 +208,30 @@ func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, is
|
||||||
friendlyRequestMethod = rm
|
friendlyRequestMethod = rm
|
||||||
}
|
}
|
||||||
|
|
||||||
if rd != "" {
|
redirectionURL := ctxGetPortalURL(ctx)
|
||||||
switch rm {
|
|
||||||
case "":
|
if redirectionURL != nil {
|
||||||
redirectionURL = fmt.Sprintf("%s?rd=%s", rd, url.QueryEscape(targetURL.String()))
|
if !utils.IsURISafeRedirection(redirectionURL, ctx.Configuration.Session.Domain) {
|
||||||
default:
|
ctx.Logger.Errorf("Configured Portal URL '%s' does not appear to be able to write cookies for the '%s' domain", redirectionURL, ctx.Configuration.Session.Domain)
|
||||||
redirectionURL = fmt.Sprintf("%s?rd=%s&rm=%s", rd, url.QueryEscape(targetURL.String()), rm)
|
|
||||||
|
ctx.ReplyUnauthorized()
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qry := redirectionURL.Query()
|
||||||
|
|
||||||
|
qry.Set("rd", targetURL.String())
|
||||||
|
|
||||||
|
if rm != "" {
|
||||||
|
qry.Set("rm", rm)
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectionURL.RawQuery = qry.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case ctx.IsXHR() || !ctx.AcceptsMIME("text/html") || rd == "":
|
case ctx.IsXHR() || !ctx.AcceptsMIME("text/html") || redirectionURL == nil:
|
||||||
statusCode = fasthttp.StatusUnauthorized
|
statusCode = fasthttp.StatusUnauthorized
|
||||||
default:
|
default:
|
||||||
switch rm {
|
switch rm {
|
||||||
|
@ -234,9 +242,9 @@ func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, is
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirectionURL != "" {
|
if redirectionURL != nil {
|
||||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d with location redirect to %s", targetURL.String(), friendlyRequestMethod, friendlyUsername, statusCode, redirectionURL)
|
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d with location redirect to %s", targetURL.String(), friendlyRequestMethod, friendlyUsername, statusCode, redirectionURL)
|
||||||
ctx.SpecialRedirect(redirectionURL, statusCode)
|
ctx.SpecialRedirect(redirectionURL.String(), statusCode)
|
||||||
} else {
|
} else {
|
||||||
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d", targetURL.String(), friendlyRequestMethod, friendlyUsername, statusCode)
|
ctx.Logger.Infof("Access to %s (method %s) is not authorized to user %s, responding with status code %d", targetURL.String(), friendlyRequestMethod, friendlyUsername, statusCode)
|
||||||
ctx.ReplyUnauthorized()
|
ctx.ReplyUnauthorized()
|
||||||
|
|
|
@ -453,7 +453,7 @@ func TestShouldRedirectWithGroups(t *testing.T) {
|
||||||
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedHost, "app.example.com")
|
mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedHost, "app.example.com")
|
||||||
mock.Ctx.Request.Header.Set("X-Forwarded-Uri", "/code-test/login")
|
mock.Ctx.Request.Header.Set("X-Forwarded-Uri", "/code-test/login")
|
||||||
|
|
||||||
mock.Ctx.Request.SetRequestURI("/?rd=https://auth.mydomain.com")
|
mock.Ctx.Request.SetRequestURI("/api/verify/?rd=https://auth.example.com")
|
||||||
|
|
||||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||||
|
|
||||||
|
@ -537,6 +537,110 @@ func (p Pair) String() string {
|
||||||
p.URL, p.Username, p.AuthenticationLevel, p.ExpectedStatusCode)
|
p.URL, p.Username, p.AuthenticationLevel, p.ExpectedStatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo // This is a test.
|
||||||
|
func TestShouldRedirectAuthorizations(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
|
||||||
|
method, originalURL, autheliaURL string
|
||||||
|
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"ShouldReturnFoundMethodNone", "", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusFound},
|
||||||
|
{"ShouldReturnFoundMethodGET", "GET", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusFound},
|
||||||
|
{"ShouldReturnFoundMethodOPTIONS", "OPTIONS", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusFound},
|
||||||
|
{"ShouldReturnSeeOtherMethodPOST", "POST", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusSeeOther},
|
||||||
|
{"ShouldReturnSeeOtherMethodPATCH", "PATCH", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusSeeOther},
|
||||||
|
{"ShouldReturnSeeOtherMethodPUT", "PUT", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusSeeOther},
|
||||||
|
{"ShouldReturnSeeOtherMethodDELETE", "DELETE", "https://one-factor.example.com/", "https://auth.example.com/", fasthttp.StatusSeeOther},
|
||||||
|
{"ShouldReturnUnauthorizedBadDomain", "GET", "https://one-factor.example.com/", "https://auth.notexample.com/", fasthttp.StatusUnauthorized},
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := VerifyGET(verifyGetCfg)
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
var (
|
||||||
|
suffix string
|
||||||
|
xhr bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
suffix += "QueryParameter"
|
||||||
|
default:
|
||||||
|
suffix += "RequestHeader"
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
switch j {
|
||||||
|
case 0:
|
||||||
|
xhr = false
|
||||||
|
case 1:
|
||||||
|
xhr = true
|
||||||
|
suffix += "XHR"
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(tc.name+suffix, func(t *testing.T) {
|
||||||
|
mock := mocks.NewMockAutheliaCtx(t)
|
||||||
|
defer mock.Close()
|
||||||
|
|
||||||
|
mock.Clock.Set(time.Now())
|
||||||
|
|
||||||
|
autheliaURL, err := url.ParseRequestURI(tc.autheliaURL)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
originalURL, err := url.ParseRequestURI(tc.originalURL)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if xhr {
|
||||||
|
mock.Ctx.Request.Header.Set(fasthttp.HeaderXRequestedWith, "XMLHttpRequest")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rm string
|
||||||
|
|
||||||
|
if tc.method != "" {
|
||||||
|
rm = fmt.Sprintf("&rm=%s", tc.method)
|
||||||
|
mock.Ctx.Request.Header.Set("X-Forwarded-Method", tc.method)
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
||||||
|
mock.Ctx.Request.Header.Set("X-Original-URL", originalURL.String())
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
mock.Ctx.Request.SetRequestURI(fmt.Sprintf("/?rd=%s", url.QueryEscape(autheliaURL.String())))
|
||||||
|
} else {
|
||||||
|
mock.Ctx.Request.Header.Set("X-Authelia-URL", autheliaURL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(mock.Ctx)
|
||||||
|
|
||||||
|
if xhr && tc.expected != fasthttp.StatusUnauthorized {
|
||||||
|
assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode())
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tc.expected, mock.Ctx.Response.StatusCode())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case xhr && tc.expected != fasthttp.StatusUnauthorized:
|
||||||
|
href := utils.StringHTMLEscape(fmt.Sprintf("%s?rd=%s%s", autheliaURL.String(), url.QueryEscape(originalURL.String()), rm))
|
||||||
|
assert.Equal(t, fmt.Sprintf("<a href=\"%s\">%d %s</a>", href, fasthttp.StatusUnauthorized, fasthttp.StatusMessage(fasthttp.StatusUnauthorized)), string(mock.Ctx.Response.Body()))
|
||||||
|
case tc.expected >= fasthttp.StatusMultipleChoices && tc.expected < fasthttp.StatusBadRequest:
|
||||||
|
href := utils.StringHTMLEscape(fmt.Sprintf("%s?rd=%s%s", autheliaURL.String(), url.QueryEscape(originalURL.String()), rm))
|
||||||
|
assert.Equal(t, fmt.Sprintf("<a href=\"%s\">%d %s</a>", href, tc.expected, fasthttp.StatusMessage(tc.expected)), string(mock.Ctx.Response.Body()))
|
||||||
|
case tc.expected < fasthttp.StatusMultipleChoices:
|
||||||
|
assert.Equal(t, utils.StringHTMLEscape(fmt.Sprintf("%d %s", tc.expected, fasthttp.StatusMessage(tc.expected))), string(mock.Ctx.Response.Body()))
|
||||||
|
default:
|
||||||
|
assert.Equal(t, utils.StringHTMLEscape(fmt.Sprintf("%d %s", tc.expected, fasthttp.StatusMessage(tc.expected))), string(mock.Ctx.Response.Body()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldVerifyAuthorizationsUsingSessionCookie(t *testing.T) {
|
func TestShouldVerifyAuthorizationsUsingSessionCookie(t *testing.T) {
|
||||||
testCases := []Pair{
|
testCases := []Pair{
|
||||||
// should apply default policy.
|
// should apply default policy.
|
||||||
|
@ -837,11 +941,36 @@ func TestShouldURLEncodeRedirectionURLParameter(t *testing.T) {
|
||||||
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
mock.Ctx.Request.Header.Set("X-Original-URL", "https://two-factor.example.com")
|
||||||
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
||||||
mock.Ctx.Request.SetHost("mydomain.com")
|
mock.Ctx.Request.SetHost("mydomain.com")
|
||||||
mock.Ctx.Request.SetRequestURI("/?rd=https://auth.mydomain.com")
|
mock.Ctx.Request.SetRequestURI("/?rd=https://auth.example.com")
|
||||||
|
|
||||||
VerifyGET(verifyGetCfg)(mock.Ctx)
|
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||||
|
|
||||||
assert.Equal(t, "<a href=\"https://auth.mydomain.com/?rd=https%3A%2F%2Ftwo-factor.example.com\">302 Found</a>",
|
assert.Equal(t, "<a href=\"https://auth.example.com/?rd=https%3A%2F%2Ftwo-factor.example.com\">302 Found</a>",
|
||||||
|
string(mock.Ctx.Response.Body()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldURLEncodeRedirectionHeader(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.Header.Set("X-Authelia-URL", "https://auth.example.com")
|
||||||
|
mock.Ctx.Request.Header.Set("Accept", "text/html; charset=utf-8")
|
||||||
|
mock.Ctx.Request.SetHost("mydomain.com")
|
||||||
|
|
||||||
|
VerifyGET(verifyGetCfg)(mock.Ctx)
|
||||||
|
|
||||||
|
assert.Equal(t, "<a href=\"https://auth.example.com/?rd=https%3A%2F%2Ftwo-factor.example.com\">302 Found</a>",
|
||||||
string(mock.Ctx.Response.Body()))
|
string(mock.Ctx.Response.Body()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bytesEmpty = []byte("")
|
||||||
|
|
||||||
|
func ctxGetPortalURL(ctx *middlewares.AutheliaCtx) (portalURL *url.URL) {
|
||||||
|
var rawURL []byte
|
||||||
|
|
||||||
|
if rawURL = ctx.QueryArgRedirect(); rawURL != nil && !bytes.Equal(rawURL, bytesEmpty) {
|
||||||
|
portalURL, _ = url.ParseRequestURI(string(rawURL))
|
||||||
|
|
||||||
|
return portalURL
|
||||||
|
} else if rawURL = ctx.XAutheliaURL(); rawURL != nil && !bytes.Equal(rawURL, bytesEmpty) {
|
||||||
|
portalURL, _ = url.ParseRequestURI(string(rawURL))
|
||||||
|
|
||||||
|
return portalURL
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -171,6 +171,16 @@ func (ctx *AutheliaCtx) XForwardedURI() (uri []byte) {
|
||||||
return uri
|
return uri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XAutheliaURL return the content of the X-Authelia-URL header.
|
||||||
|
func (ctx *AutheliaCtx) XAutheliaURL() (autheliaURL []byte) {
|
||||||
|
return ctx.RequestCtx.Request.Header.PeekBytes(headerXAutheliaURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryArgRedirect return the content of the rd query argument.
|
||||||
|
func (ctx *AutheliaCtx) QueryArgRedirect() (val []byte) {
|
||||||
|
return ctx.RequestCtx.QueryArgs().PeekBytes(queryArgRedirect)
|
||||||
|
}
|
||||||
|
|
||||||
// BasePath returns the base_url as per the path visited by the client.
|
// BasePath returns the base_url as per the path visited by the client.
|
||||||
func (ctx *AutheliaCtx) BasePath() (base string) {
|
func (ctx *AutheliaCtx) BasePath() (base string) {
|
||||||
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
|
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
headerXAutheliaURL = []byte("X-Authelia-URL")
|
||||||
|
|
||||||
headerAccept = []byte(fasthttp.HeaderAccept)
|
headerAccept = []byte(fasthttp.HeaderAccept)
|
||||||
headerContentLength = []byte(fasthttp.HeaderContentLength)
|
headerContentLength = []byte(fasthttp.HeaderContentLength)
|
||||||
headerLocation = []byte(fasthttp.HeaderLocation)
|
headerLocation = []byte(fasthttp.HeaderLocation)
|
||||||
|
@ -71,6 +73,8 @@ var (
|
||||||
protoHTTPS = []byte(strProtoHTTPS)
|
protoHTTPS = []byte(strProtoHTTPS)
|
||||||
protoHTTP = []byte(strProtoHTTP)
|
protoHTTP = []byte(strProtoHTTP)
|
||||||
|
|
||||||
|
queryArgRedirect = []byte("rd")
|
||||||
|
|
||||||
// UserValueKeyBaseURL is the User Value key where we store the Base URL.
|
// UserValueKeyBaseURL is the User Value key where we store the Base URL.
|
||||||
UserValueKeyBaseURL = []byte("base_url")
|
UserValueKeyBaseURL = []byte("base_url")
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,9 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
|
||||||
r.GET("/api/verify", middlewares.Wrap(metricsVRMW, middleware(handlers.VerifyGET(config.AuthenticationBackend))))
|
r.GET("/api/verify", middlewares.Wrap(metricsVRMW, middleware(handlers.VerifyGET(config.AuthenticationBackend))))
|
||||||
r.HEAD("/api/verify", middlewares.Wrap(metricsVRMW, middleware(handlers.VerifyGET(config.AuthenticationBackend))))
|
r.HEAD("/api/verify", middlewares.Wrap(metricsVRMW, middleware(handlers.VerifyGET(config.AuthenticationBackend))))
|
||||||
|
|
||||||
|
r.GET("/api/verify/{path:*}", middlewares.Wrap(metricsVRMW, middleware(handlers.VerifyGET(config.AuthenticationBackend))))
|
||||||
|
r.HEAD("/api/verify/{path:*}", middlewares.Wrap(metricsVRMW, middleware(handlers.VerifyGET(config.AuthenticationBackend))))
|
||||||
|
|
||||||
r.POST("/api/checks/safe-redirection", middlewareAPI(handlers.CheckSafeRedirectionPOST))
|
r.POST("/api/checks/safe-redirection", middlewareAPI(handlers.CheckSafeRedirectionPOST))
|
||||||
|
|
||||||
delayFunc := middlewares.TimingAttackDelay(10, 250, 85, time.Second, true)
|
delayFunc := middlewares.TimingAttackDelay(10, 250, 85, time.Second, true)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
###############################################################
|
||||||
|
# Authelia minimal configuration #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
jwt_secret: unsecure_secret
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 9091
|
||||||
|
asset_path: /config/assets/
|
||||||
|
tls:
|
||||||
|
certificate: /config/ssl/cert.pem
|
||||||
|
key: /config/ssl/key.pem
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: debug
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: /config/users.yml
|
||||||
|
|
||||||
|
session:
|
||||||
|
secret: unsecure_session_secret
|
||||||
|
domain: example.com
|
||||||
|
expiration: 3600 # 1 hour
|
||||||
|
inactivity: 300 # 5 minutes
|
||||||
|
remember_me_duration: 1y
|
||||||
|
|
||||||
|
storage:
|
||||||
|
encryption_key: a_not_so_secure_encryption_key
|
||||||
|
local:
|
||||||
|
path: /config/db.sqlite
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: bypass
|
||||||
|
rules:
|
||||||
|
- domain: "login.example.com"
|
||||||
|
policy: bypass
|
||||||
|
- domain: "public.example.com"
|
||||||
|
policy: bypass
|
||||||
|
- domain: "admin.example.com"
|
||||||
|
policy: two_factor
|
||||||
|
- domain: "secure.example.com"
|
||||||
|
policy: two_factor
|
||||||
|
- domain: "singlefactor.example.com"
|
||||||
|
policy: one_factor
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
smtp:
|
||||||
|
host: smtp
|
||||||
|
port: 1025
|
||||||
|
sender: admin@example.com
|
||||||
|
disable_require_tls: true
|
||||||
|
...
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
authelia-backend:
|
||||||
|
volumes:
|
||||||
|
- './Envoy/configuration.yml:/config/configuration.yml:ro'
|
||||||
|
- './Envoy/users.yml:/config/users.yml'
|
||||||
|
- './common/ssl:/config/ssl:ro'
|
||||||
|
...
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
###############################################################
|
||||||
|
# Users Database #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
# This file can be used if you do not have an LDAP set up.
|
||||||
|
|
||||||
|
# List of users
|
||||||
|
users:
|
||||||
|
john:
|
||||||
|
displayname: "John Doe"
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
|
||||||
|
email: john.doe@authelia.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
|
- dev
|
||||||
|
|
||||||
|
harry:
|
||||||
|
displayname: "Harry Potter"
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
|
||||||
|
email: harry.potter@authelia.com
|
||||||
|
groups: []
|
||||||
|
|
||||||
|
bob:
|
||||||
|
displayname: "Bob Dylan"
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
|
||||||
|
email: bob.dylan@authelia.com
|
||||||
|
groups:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
james:
|
||||||
|
displayname: "James Dean"
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" # yamllint disable-line rule:line-length
|
||||||
|
email: james.dean@authelia.com
|
||||||
|
...
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
envoy:
|
||||||
|
image: envoyproxy/envoy:v1.23.0
|
||||||
|
volumes:
|
||||||
|
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
|
||||||
|
- ./example/compose/nginx/portal/ssl:/etc/ssl
|
||||||
|
networks:
|
||||||
|
authelianet:
|
||||||
|
ipv4_address: 192.168.240.100
|
||||||
|
...
|
|
@ -0,0 +1,236 @@
|
||||||
|
---
|
||||||
|
static_resources:
|
||||||
|
listeners:
|
||||||
|
- name: listener_0
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: 0.0.0.0
|
||||||
|
port_value: 8080
|
||||||
|
filter_chains:
|
||||||
|
- filters:
|
||||||
|
- name: envoy.filters.network.http_connection_manager
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager # yamllint disable-line rule:line-length
|
||||||
|
stat_prefix: ingress_http
|
||||||
|
use_remote_address: true
|
||||||
|
skip_xff_append: false
|
||||||
|
access_log:
|
||||||
|
- name: envoy.access_loggers.stdout
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
|
||||||
|
route_config:
|
||||||
|
name: local_route
|
||||||
|
virtual_hosts:
|
||||||
|
- name: login_service
|
||||||
|
domains: ["login.example.com:8080"]
|
||||||
|
typed_per_filter_config:
|
||||||
|
envoy.filters.http.ext_authz:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||||
|
disabled: true
|
||||||
|
routes:
|
||||||
|
- match:
|
||||||
|
prefix: "/.well-known/"
|
||||||
|
route:
|
||||||
|
cluster: authelia-backend
|
||||||
|
- match:
|
||||||
|
prefix: "/api/"
|
||||||
|
route:
|
||||||
|
cluster: authelia-backend
|
||||||
|
- match:
|
||||||
|
prefix: "/locales/"
|
||||||
|
route:
|
||||||
|
cluster: authelia-backend
|
||||||
|
- match:
|
||||||
|
path: "/jwks.json"
|
||||||
|
route:
|
||||||
|
cluster: authelia-backend
|
||||||
|
- match:
|
||||||
|
prefix: "/"
|
||||||
|
route:
|
||||||
|
cluster: authelia-frontend
|
||||||
|
- name: mail_service
|
||||||
|
domains: ["mail.example.com:8080"]
|
||||||
|
typed_per_filter_config:
|
||||||
|
envoy.filters.http.ext_authz:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||||
|
disabled: true
|
||||||
|
routes:
|
||||||
|
- match:
|
||||||
|
prefix: "/"
|
||||||
|
route:
|
||||||
|
cluster: smtp
|
||||||
|
- name: http_service
|
||||||
|
domains: ["*.example.com:8080"]
|
||||||
|
routes:
|
||||||
|
- match:
|
||||||
|
prefix: "/headers"
|
||||||
|
route:
|
||||||
|
cluster: httpbin
|
||||||
|
- match:
|
||||||
|
prefix: "/"
|
||||||
|
route:
|
||||||
|
cluster: nginx-backend
|
||||||
|
http_filters:
|
||||||
|
- name: envoy.filters.http.ext_authz
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
|
||||||
|
http_service:
|
||||||
|
path_prefix: /api/verify/
|
||||||
|
server_uri:
|
||||||
|
uri: authelia-backend:9091
|
||||||
|
cluster: authelia-backend
|
||||||
|
timeout: 0.25s
|
||||||
|
authorization_request:
|
||||||
|
allowed_headers:
|
||||||
|
patterns:
|
||||||
|
- exact: accept
|
||||||
|
- exact: cookie
|
||||||
|
- exact: proxy-authorization
|
||||||
|
headers_to_add:
|
||||||
|
- key: X-Authelia-URL
|
||||||
|
value: 'https://login.example.com:8080/'
|
||||||
|
- key: X-Forwarded-Method
|
||||||
|
value: '%REQ(:METHOD)%'
|
||||||
|
- key: X-Forwarded-Proto
|
||||||
|
value: '%REQ(:SCHEME)%'
|
||||||
|
- key: X-Forwarded-Host
|
||||||
|
value: '%REQ(:AUTHORITY)%'
|
||||||
|
- key: X-Forwarded-URI
|
||||||
|
value: '%REQ(:PATH)%'
|
||||||
|
- key: X-Forwarded-For
|
||||||
|
value: '%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%'
|
||||||
|
authorization_response:
|
||||||
|
allowed_upstream_headers:
|
||||||
|
patterns:
|
||||||
|
- prefix: remote-
|
||||||
|
allowed_client_headers:
|
||||||
|
patterns:
|
||||||
|
- exact: set-cookie
|
||||||
|
allowed_client_headers_on_success:
|
||||||
|
patterns:
|
||||||
|
- exact: set-cookie
|
||||||
|
failure_mode_allow: false
|
||||||
|
- name: envoy.filters.http.router
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||||
|
transport_socket:
|
||||||
|
name: envoy.transport_sockets.tls
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
|
||||||
|
common_tls_context:
|
||||||
|
tls_certificates:
|
||||||
|
- certificate_chain:
|
||||||
|
filename: /etc/ssl/server.cert
|
||||||
|
private_key:
|
||||||
|
filename: /etc/ssl/server.key
|
||||||
|
clusters:
|
||||||
|
- name: authelia-frontend
|
||||||
|
transport_socket_matches:
|
||||||
|
- name: "enableTLS"
|
||||||
|
match:
|
||||||
|
enableTLS: true
|
||||||
|
transport_socket:
|
||||||
|
name: envoy.transport_sockets.tls
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
|
||||||
|
common_tls_context: {}
|
||||||
|
- name: "defaultTLSDisabled"
|
||||||
|
match: {}
|
||||||
|
transport_socket:
|
||||||
|
name: envoy.transport_sockets.raw_buffer
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: STRICT_DNS
|
||||||
|
dns_lookup_family: V4_ONLY
|
||||||
|
lb_policy: ROUND_ROBIN
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: authelia-frontend
|
||||||
|
endpoints:
|
||||||
|
- locality:
|
||||||
|
region: dev
|
||||||
|
priority: 0
|
||||||
|
lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
health_check_config:
|
||||||
|
hostname: authelia-frontend
|
||||||
|
port_value: 3000
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: authelia-frontend
|
||||||
|
port_value: 3000
|
||||||
|
- locality:
|
||||||
|
region: ci
|
||||||
|
priority: 1
|
||||||
|
lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: authelia-backend
|
||||||
|
port_value: 9091
|
||||||
|
metadata:
|
||||||
|
filter_metadata:
|
||||||
|
envoy.transport_socket_match:
|
||||||
|
enableTLS: true
|
||||||
|
- name: authelia-backend
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: LOGICAL_DNS
|
||||||
|
dns_lookup_family: V4_ONLY
|
||||||
|
lb_policy: ROUND_ROBIN
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: authelia-backend
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: authelia-backend
|
||||||
|
port_value: 9091
|
||||||
|
transport_socket:
|
||||||
|
name: envoy.transport_sockets.tls
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
|
||||||
|
common_tls_context: {}
|
||||||
|
- name: smtp
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: LOGICAL_DNS
|
||||||
|
dns_lookup_family: V4_ONLY
|
||||||
|
lb_policy: ROUND_ROBIN
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: smtp
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: smtp
|
||||||
|
port_value: 1080
|
||||||
|
- name: httpbin
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: LOGICAL_DNS
|
||||||
|
dns_lookup_family: V4_ONLY
|
||||||
|
lb_policy: ROUND_ROBIN
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: httpbin
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: httpbin
|
||||||
|
port_value: 8000
|
||||||
|
- name: nginx-backend
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: LOGICAL_DNS
|
||||||
|
dns_lookup_family: V4_ONLY
|
||||||
|
lb_policy: ROUND_ROBIN
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: nginx-backend
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: nginx-backend
|
||||||
|
port_value: 80
|
||||||
|
...
|
|
@ -0,0 +1,84 @@
|
||||||
|
package suites
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var envoySuiteName = "Envoy"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dockerEnvironment := NewDockerEnvironment([]string{
|
||||||
|
"internal/suites/docker-compose.yml",
|
||||||
|
"internal/suites/Envoy/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/authelia/docker-compose.backend.{}.yml",
|
||||||
|
"internal/suites/example/compose/authelia/docker-compose.frontend.{}.yml",
|
||||||
|
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/envoy/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/smtp/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/httpbin/docker-compose.yml",
|
||||||
|
})
|
||||||
|
|
||||||
|
if os.Getenv("CI") == t {
|
||||||
|
dockerEnvironment = NewDockerEnvironment([]string{
|
||||||
|
"internal/suites/docker-compose.yml",
|
||||||
|
"internal/suites/Envoy/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/authelia/docker-compose.backend.{}.yml",
|
||||||
|
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/envoy/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/smtp/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/httpbin/docker-compose.yml",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setup := func(suitePath string) error {
|
||||||
|
if err := dockerEnvironment.Up(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitUntilAutheliaIsReady(dockerEnvironment, envoySuiteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
displayAutheliaLogs := func() error {
|
||||||
|
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(backendLogs)
|
||||||
|
|
||||||
|
if os.Getenv("CI") != t {
|
||||||
|
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(frontendLogs)
|
||||||
|
}
|
||||||
|
|
||||||
|
envoyLogs, err := dockerEnvironment.Logs("envoy", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(envoyLogs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown := func(suitePath string) error {
|
||||||
|
err := dockerEnvironment.Down()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalRegistry.Register(envoySuiteName, Suite{
|
||||||
|
SetUp: setup,
|
||||||
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: displayAutheliaLogs,
|
||||||
|
OnError: displayAutheliaLogs,
|
||||||
|
TestTimeout: 2 * time.Minute,
|
||||||
|
TearDown: teardown,
|
||||||
|
TearDownTimeout: 2 * time.Minute,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package suites
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnvoySuite struct {
|
||||||
|
*RodSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnvoySuite() *EnvoySuite {
|
||||||
|
return &EnvoySuite{RodSuite: new(RodSuite)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvoySuite) Test1FAScenario() {
|
||||||
|
suite.Run(s.T(), New1FAScenario())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvoySuite) Test2FAScenario() {
|
||||||
|
suite.Run(s.T(), New2FAScenario())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvoySuite) TestCustomHeaders() {
|
||||||
|
suite.Run(s.T(), NewCustomHeadersScenario())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EnvoySuite) TestResetPasswordScenario() {
|
||||||
|
suite.Run(s.T(), NewResetPasswordScenario())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvoySuite(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping suite test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.Run(t, NewEnvoySuite())
|
||||||
|
}
|
Loading…
Reference in New Issue