mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-24 14:37:12 +02:00
authorize: fix headers when impersonating
- Add user impersonation docs. - Add navbar link to v0.0.5 docs.
This commit is contained in:
parent
fb92466f45
commit
554e62108f
8 changed files with 134 additions and 21 deletions
|
@ -24,6 +24,7 @@ module.exports = {
|
||||||
{
|
{
|
||||||
text: 'Versions',
|
text: 'Versions',
|
||||||
items: [
|
items: [
|
||||||
|
{ text: 'v0.0.5', link: 'https://v0-0-5.docs.pomerium.io/' },
|
||||||
{ text: 'v0.0.4', link: 'https://v0-0-4.docs.pomerium.io/' },
|
{ text: 'v0.0.4', link: 'https://v0-0-4.docs.pomerium.io/' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -52,7 +53,7 @@ function docsSidebar(title) {
|
||||||
{
|
{
|
||||||
title,
|
title,
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
children: ["", "identity-providers", "signed-headers", "certificates", "examples", "upgrading"]
|
children: ["", "identity-providers", "signed-headers", "certificates", "examples", "impersonation", "upgrading"]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
43
docs/docs/impersonation.md
Normal file
43
docs/docs/impersonation.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
title: User impersonation
|
||||||
|
description: >-
|
||||||
|
This article describes how to configure Pomerium to allow an administrative
|
||||||
|
user to impersonate another user or group.
|
||||||
|
---
|
||||||
|
|
||||||
|
# User impersonation
|
||||||
|
|
||||||
|
## What
|
||||||
|
|
||||||
|
User impersonation allows administrative users to temporarily "sign in as" another user in pomerium. Users with impersonation permissions can impersonate all other users and groups. The impersonating user will be subject to the authorization and access policies of the impersonated user.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
In certain circumstances, it's useful for an administrative user to impersonate another user. For example:
|
||||||
|
|
||||||
|
- To help a user troubleshoot an issue. If your downstream authorization policies are configured differently, it's possible that your UI will look different from theirs and you'll need to impersonate the other user to be able to see what they see.
|
||||||
|
- You want to make changes on behalf of another user (for example, the other user is away on vacation and you want to manage their orders or run a report).
|
||||||
|
- You're an administrator who's setting up authorization policies, and you want to preview what other users will be able to see depending on the permissions you grant them.
|
||||||
|
|
||||||
|
## How
|
||||||
|
|
||||||
|
1. Add an administrator to your [configuration settings].
|
||||||
|
2. Navigate to user dashboard for any proxied route. (e.g. `https://{your-domain}/.pomerium`)
|
||||||
|
3. Add the `email` and `user groups` you want to impersonate.
|
||||||
|
4. That's it!
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
**Note!** On session refresh, impersonation will be reset.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Here's what it looks like.
|
||||||
|
|
||||||
|
<video width="100%" height="600" controls=""><source src="./impersonation/pomerium-user-impersonation.mp4" type="video/mp4">
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
|
||||||
|
[configuration settings]: ../reference/#administrators
|
BIN
docs/docs/impersonation/pomerium-user-impersonation.mp4
Normal file
BIN
docs/docs/impersonation/pomerium-user-impersonation.mp4
Normal file
Binary file not shown.
|
@ -36,6 +36,28 @@ func (s *SessionState) RefreshPeriodExpired() bool {
|
||||||
return isExpired(s.RefreshDeadline)
|
return isExpired(s.RefreshDeadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Impersonating returns if the request is impersonating.
|
||||||
|
func (s *SessionState) Impersonating() bool {
|
||||||
|
return s.ImpersonateEmail != "" || len(s.ImpersonateGroups) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestEmail is the email to make the request as.
|
||||||
|
func (s *SessionState) RequestEmail() string {
|
||||||
|
if s.ImpersonateEmail != "" {
|
||||||
|
return s.ImpersonateEmail
|
||||||
|
}
|
||||||
|
return s.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestGroups returns the groups of the Groups making the request; uses
|
||||||
|
// impersonating user if set.
|
||||||
|
func (s *SessionState) RequestGroups() string {
|
||||||
|
if len(s.ImpersonateGroups) != 0 {
|
||||||
|
return strings.Join(s.ImpersonateGroups, ",")
|
||||||
|
}
|
||||||
|
return strings.Join(s.Groups, ",")
|
||||||
|
}
|
||||||
|
|
||||||
type idToken struct {
|
type idToken struct {
|
||||||
Issuer string `json:"iss"`
|
Issuer string `json:"iss"`
|
||||||
Subject string `json:"sub"`
|
Subject string `json:"sub"`
|
||||||
|
|
|
@ -100,3 +100,41 @@ func TestSessionState_IssuedAt(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSessionState_Impersonating(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
Email string
|
||||||
|
Groups []string
|
||||||
|
ImpersonateEmail string
|
||||||
|
ImpersonateGroups []string
|
||||||
|
want bool
|
||||||
|
wantResponseEmail string
|
||||||
|
wantResponseGroups string
|
||||||
|
}{
|
||||||
|
{"impersonating", "actual@user.com", []string{"actual-group"}, "impersonating@user.com", []string{"impersonating-group"}, true, "impersonating@user.com", "impersonating-group"},
|
||||||
|
{"not impersonating", "actual@user.com", []string{"actual-group"}, "", []string{}, false, "actual@user.com", "actual-group"},
|
||||||
|
{"impersonating user only", "actual@user.com", []string{"actual-group"}, "impersonating@user.com", []string{}, true, "impersonating@user.com", "actual-group"},
|
||||||
|
{"impersonating group only", "actual@user.com", []string{"actual-group"}, "", []string{"impersonating-group"}, true, "actual@user.com", "impersonating-group"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &SessionState{
|
||||||
|
Email: tt.Email,
|
||||||
|
Groups: tt.Groups,
|
||||||
|
ImpersonateEmail: tt.ImpersonateEmail,
|
||||||
|
ImpersonateGroups: tt.ImpersonateGroups,
|
||||||
|
}
|
||||||
|
if got := s.Impersonating(); got != tt.want {
|
||||||
|
t.Errorf("SessionState.Impersonating() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
if gotEmail := s.RequestEmail(); gotEmail != tt.wantResponseEmail {
|
||||||
|
t.Errorf("SessionState.RequestEmail() = %v, want %v", gotEmail, tt.wantResponseEmail)
|
||||||
|
}
|
||||||
|
if gotGroups := s.RequestGroups(); gotGroups != tt.wantResponseGroups {
|
||||||
|
t.Errorf("SessionState.v() = %v, want %v", gotGroups, tt.wantResponseGroups)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ func TestAuthorizeGRPC_Authorize(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"good", "hello.pomerium.io", &sessions.SessionState{User: "admin@pomerium.io", Email: "admin@pomerium.io"}, true, false},
|
{"good", "hello.pomerium.io", &sessions.SessionState{User: "admin@pomerium.io", Email: "admin@pomerium.io"}, true, false},
|
||||||
|
{"impersonate request", "hello.pomerium.io", &sessions.SessionState{User: "admin@pomerium.io", Email: "admin@pomerium.io", ImpersonateEmail: "other@other.example"}, true, false},
|
||||||
{"session cannot be nil", "hello.pomerium.io", nil, false, true},
|
{"session cannot be nil", "hello.pomerium.io", nil, false, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -214,7 +214,7 @@ func isCORSPreflight(r *http.Request) bool {
|
||||||
// or starting the authenticate service for validation if not.
|
// or starting the authenticate service for validation if not.
|
||||||
func (p *Proxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
func (p *Proxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
||||||
if !p.shouldSkipAuthentication(r) {
|
if !p.shouldSkipAuthentication(r) {
|
||||||
session, err := p.sessionStore.LoadSession(r)
|
s, err := p.sessionStore.LoadSession(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case http.ErrNoCookie, sessions.ErrLifetimeExpired, sessions.ErrInvalidSession:
|
case http.ErrNoCookie, sessions.ErrLifetimeExpired, sessions.ErrInvalidSession:
|
||||||
|
@ -230,23 +230,30 @@ func (p *Proxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = p.authenticate(w, r, session); err != nil {
|
if err = p.authenticate(w, r, s); err != nil {
|
||||||
p.sessionStore.ClearSession(w, r)
|
p.sessionStore.ClearSession(w, r)
|
||||||
log.Debug().Err(err).Msg("proxy: user unauthenticated")
|
log.FromRequest(r).Debug().Err(err).Msg("proxy: user unauthenticated")
|
||||||
httpErr := &httputil.Error{
|
httpErr := &httputil.Error{Message: "User unauthenticated", Code: http.StatusForbidden, CanDebug: true}
|
||||||
Message: "User unauthenticated",
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
CanDebug: true}
|
|
||||||
httputil.ErrorResponse(w, r, httpErr)
|
httputil.ErrorResponse(w, r, httpErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
authorized, err := p.AuthorizeClient.Authorize(r.Context(), r.Host, session)
|
authorized, err := p.AuthorizeClient.Authorize(r.Context(), r.Host, s)
|
||||||
if err != nil || !authorized {
|
if err != nil {
|
||||||
|
log.FromRequest(r).Error().Err(err).Msg("proxy: failed authorization")
|
||||||
|
httpErr := &httputil.Error{Code: http.StatusInternalServerError}
|
||||||
|
httputil.ErrorResponse(w, r, httpErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !authorized {
|
||||||
log.FromRequest(r).Warn().Err(err).Msg("proxy: user unauthorized")
|
log.FromRequest(r).Warn().Err(err).Msg("proxy: user unauthorized")
|
||||||
httpErr := &httputil.Error{Code: http.StatusUnauthorized, CanDebug: true}
|
httpErr := &httputil.Error{Code: http.StatusUnauthorized, CanDebug: true}
|
||||||
httputil.ErrorResponse(w, r, httpErr)
|
httputil.ErrorResponse(w, r, httpErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
r.Header.Set(HeaderUserID, s.User)
|
||||||
|
r.Header.Set(HeaderEmail, s.RequestEmail())
|
||||||
|
r.Header.Set(HeaderGroups, s.RequestGroups())
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have validated the users request and now proxy their request to the provided upstream.
|
// We have validated the users request and now proxy their request to the provided upstream.
|
||||||
|
@ -324,7 +331,7 @@ func (p *Proxy) UserDashboard(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh redeems and extends an existing authenticated oidc session with
|
// Refresh redeems and extends an existing authenticated oidc session with
|
||||||
// the underlying idenity provider. All session details including groups,
|
// the underlying identity provider. All session details including groups,
|
||||||
// timeouts, will be renewed.
|
// timeouts, will be renewed.
|
||||||
func (p *Proxy) Refresh(w http.ResponseWriter, r *http.Request) {
|
func (p *Proxy) Refresh(w http.ResponseWriter, r *http.Request) {
|
||||||
session, err := p.sessionStore.LoadSession(r)
|
session, err := p.sessionStore.LoadSession(r)
|
||||||
|
@ -436,25 +443,22 @@ func (p *Proxy) Impersonate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Authenticate authenticates a request by checking for a session cookie, and validating its expiration,
|
// Authenticate authenticates a request by checking for a session cookie, and validating its expiration,
|
||||||
// clearing the session cookie if it's invalid and returning an error if necessary..
|
// clearing the session cookie if it's invalid and returning an error if necessary..
|
||||||
func (p *Proxy) authenticate(w http.ResponseWriter, r *http.Request, session *sessions.SessionState) error {
|
func (p *Proxy) authenticate(w http.ResponseWriter, r *http.Request, s *sessions.SessionState) error {
|
||||||
if session.RefreshPeriodExpired() {
|
if s.RefreshPeriodExpired() {
|
||||||
session, err := p.AuthenticateClient.Refresh(r.Context(), session)
|
s, err := p.AuthenticateClient.Refresh(r.Context(), s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("proxy: session refresh failed : %v", err)
|
return fmt.Errorf("proxy: session refresh failed : %v", err)
|
||||||
}
|
}
|
||||||
err = p.sessionStore.SaveSession(w, r, session)
|
err = p.sessionStore.SaveSession(w, r, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("proxy: refresh failed : %v", err)
|
return fmt.Errorf("proxy: refresh failed : %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
valid, err := p.AuthenticateClient.Validate(r.Context(), session.IDToken)
|
valid, err := p.AuthenticateClient.Validate(r.Context(), s.IDToken)
|
||||||
if err != nil || !valid {
|
if err != nil || !valid {
|
||||||
return fmt.Errorf("proxy: session valid: %v : %v", valid, err)
|
return fmt.Errorf("proxy: session valid: %v : %v", valid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.Header.Set(HeaderUserID, session.User)
|
|
||||||
r.Header.Set(HeaderEmail, session.Email)
|
|
||||||
r.Header.Set(HeaderGroups, strings.Join(session.Groups, ","))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,8 @@ func TestProxy_Proxy(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{"good", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusOK},
|
{"good", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusOK},
|
||||||
{"good cors preflight", optsCORS, http.MethodOptions, goodCORSHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: false}, http.StatusOK},
|
{"good cors preflight", optsCORS, http.MethodOptions, goodCORSHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: false}, http.StatusOK},
|
||||||
|
{"good email impersonation", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: &sessions.SessionState{AccessToken: "AccessToken", RefreshToken: "RefreshToken", RefreshDeadline: time.Now().Add(10 * time.Second), ImpersonateEmail: "test@user.example"}}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusOK},
|
||||||
|
{"good group impersonation", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: &sessions.SessionState{AccessToken: "AccessToken", RefreshToken: "RefreshToken", RefreshDeadline: time.Now().Add(10 * time.Second), ImpersonateGroups: []string{"group1", "group2"}}}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusOK},
|
||||||
// same request as above, but with cors_allow_preflight=false in the policy
|
// same request as above, but with cors_allow_preflight=false in the policy
|
||||||
{"valid cors, but not allowed", opts, http.MethodOptions, goodCORSHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: false}, http.StatusUnauthorized},
|
{"valid cors, but not allowed", opts, http.MethodOptions, goodCORSHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: false}, http.StatusUnauthorized},
|
||||||
// cors allowed, but the request is missing proper headers
|
// cors allowed, but the request is missing proper headers
|
||||||
|
@ -308,7 +310,8 @@ func TestProxy_Proxy(t *testing.T) {
|
||||||
{"unexpected error", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{LoadError: errors.New("ok")}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusInternalServerError},
|
{"unexpected error", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{LoadError: errors.New("ok")}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusInternalServerError},
|
||||||
// redirect to start auth process
|
// redirect to start auth process
|
||||||
{"unknown host", opts, http.MethodGet, defaultHeaders, "https://nothttpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusNotFound},
|
{"unknown host", opts, http.MethodGet, defaultHeaders, "https://nothttpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusNotFound},
|
||||||
{"user forbidden", opts, http.MethodGet, defaultHeaders, "https://nothttpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: false}, http.StatusUnauthorized},
|
{"user not authorized", opts, http.MethodGet, defaultHeaders, "https://nothttpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: false}, http.StatusUnauthorized},
|
||||||
|
{"authorization call failed", opts, http.MethodGet, defaultHeaders, "https://nothttpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeError: errors.New("error")}, http.StatusInternalServerError},
|
||||||
// authenticate errors
|
// authenticate errors
|
||||||
{"weird load session error", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{LoadError: errors.New("weird"), Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusInternalServerError},
|
{"weird load session error", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{LoadError: errors.New("weird"), Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusInternalServerError},
|
||||||
{"failed refreshed session", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: &sessions.SessionState{RefreshDeadline: time.Now().Add(-10 * time.Second)}}, clients.MockAuthenticate{RefreshError: errors.New("refresh error")}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusForbidden},
|
{"failed refreshed session", opts, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: &sessions.SessionState{RefreshDeadline: time.Now().Add(-10 * time.Second)}}, clients.MockAuthenticate{RefreshError: errors.New("refresh error")}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusForbidden},
|
||||||
|
@ -473,6 +476,7 @@ func TestProxy_Impersonate(t *testing.T) {
|
||||||
{"decrypted csrf mismatch", false, opts, http.MethodPost, "user@blah.com", "", "CSRF!", &cryptutil.MockCipher{}, &sessions.MockSessionStore{Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusForbidden},
|
{"decrypted csrf mismatch", false, opts, http.MethodPost, "user@blah.com", "", "CSRF!", &cryptutil.MockCipher{}, &sessions.MockSessionStore{Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusForbidden},
|
||||||
{"save session failure", false, opts, http.MethodPost, "user@blah.com", "", "", &cryptutil.MockCipher{}, &sessions.MockSessionStore{SaveError: errors.New("err"), Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusInternalServerError},
|
{"save session failure", false, opts, http.MethodPost, "user@blah.com", "", "", &cryptutil.MockCipher{}, &sessions.MockSessionStore{SaveError: errors.New("err"), Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusInternalServerError},
|
||||||
{"malformed", true, opts, http.MethodPost, "user@blah.com", "", "", &cryptutil.MockCipher{}, &sessions.MockSessionStore{Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusBadRequest},
|
{"malformed", true, opts, http.MethodPost, "user@blah.com", "", "", &cryptutil.MockCipher{}, &sessions.MockSessionStore{Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusBadRequest},
|
||||||
|
{"groups", false, opts, http.MethodPost, "user@blah.com", "group1,group2", "", &cryptutil.MockCipher{}, &sessions.MockSessionStore{Session: &sessions.SessionState{Email: "user@test.example", IDToken: ""}}, &sessions.MockCSRFStore{Cookie: &http.Cookie{Value: "csrf"}}, clients.MockAuthenticate{}, clients.MockAuthorize{IsAdminResponse: true}, http.StatusFound},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue