mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 10:26:29 +02:00
authenticate: move impersonate from proxy to authenticate (#965)
This commit is contained in:
parent
99142b7293
commit
8362f18355
6 changed files with 27 additions and 73 deletions
|
@ -78,6 +78,7 @@ func (a *Authenticate) Mount(r *mux.Router) {
|
|||
v.Path("/").Handler(httputil.HandlerFunc(a.Dashboard))
|
||||
v.Path("/sign_in").Handler(httputil.HandlerFunc(a.SignIn))
|
||||
v.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
|
||||
v.Path("/admin/impersonate").Handler(httputil.HandlerFunc(a.Impersonate)).Methods(http.MethodPost)
|
||||
|
||||
wk := r.PathPrefix("/.well-known/pomerium").Subrouter()
|
||||
wk.Path("/jwks.json").Handler(httputil.HandlerFunc(a.jwks)).Methods(http.MethodGet)
|
||||
|
@ -284,6 +285,29 @@ func (a *Authenticate) SignOut(w http.ResponseWriter, r *http.Request) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Impersonate takes the result of a form and adds user impersonation details
|
||||
// to the user's current user sessions state if the user is currently an
|
||||
// administrative user. Requests are redirected back to the user dashboard.
|
||||
func (a *Authenticate) Impersonate(w http.ResponseWriter, r *http.Request) error {
|
||||
redirectURL := urlutil.GetAbsoluteURL(r).ResolveReference(&url.URL{
|
||||
Path: "/.pomerium",
|
||||
})
|
||||
if u, err := url.Parse(r.FormValue(urlutil.QueryRedirectURI)); err == nil {
|
||||
redirectURL = u
|
||||
}
|
||||
signinURL := urlutil.GetAbsoluteURL(r).ResolveReference(&url.URL{
|
||||
Path: "/.pomerium/sign_in",
|
||||
})
|
||||
q := signinURL.Query()
|
||||
q.Set(urlutil.QueryRedirectURI, redirectURL.String())
|
||||
q.Set(urlutil.QueryImpersonateAction, r.FormValue(urlutil.QueryImpersonateAction))
|
||||
q.Set(urlutil.QueryImpersonateEmail, r.FormValue(urlutil.QueryImpersonateEmail))
|
||||
q.Set(urlutil.QueryImpersonateGroups, r.FormValue(urlutil.QueryImpersonateGroups))
|
||||
signinURL.RawQuery = q.Encode()
|
||||
httputil.Redirect(w, r, urlutil.NewSignedURL(a.sharedKey, signinURL).String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// reauthenticateOrFail starts the authenticate process by redirecting the
|
||||
// user to their respective identity provider. This function also builds the
|
||||
// 'state' parameter which is encrypted and includes authenticating data
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -181,6 +181,7 @@
|
|||
</div>
|
||||
|
||||
<form method="POST" action="/.pomerium/admin/impersonate">
|
||||
<input type="hidden" value="{{.RedirectURL}}" name="pomerium_redirect_uri">
|
||||
<section>
|
||||
<p class="message">
|
||||
Administrators can temporarily impersonate another user.
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -34,9 +34,6 @@ func (p *Proxy) registerDashboardHandlers(r *mux.Router) *mux.Router {
|
|||
// dashboard endpoints can be used by user's to view, or modify their session
|
||||
h.Path("/").HandlerFunc(p.UserDashboard).Methods(http.MethodGet)
|
||||
h.Path("/sign_out").HandlerFunc(p.SignOut).Methods(http.MethodGet, http.MethodPost)
|
||||
// admin endpoints authorization is also delegated to authorizer service
|
||||
admin := h.PathPrefix("/admin").Subrouter()
|
||||
admin.Path("/impersonate").Handler(httputil.HandlerFunc(p.Impersonate)).Methods(http.MethodPost)
|
||||
|
||||
// Authenticate service callback handlers and middleware
|
||||
// callback used to set route-scoped session and redirect back to destination
|
||||
|
@ -100,23 +97,6 @@ func (p *Proxy) UserDashboard(w http.ResponseWriter, r *http.Request) {
|
|||
httputil.Redirect(w, r, url.String(), http.StatusFound)
|
||||
}
|
||||
|
||||
// Impersonate takes the result of a form and adds user impersonation details
|
||||
// to the user's current user sessions state if the user is currently an
|
||||
// administrative user. Requests are redirected back to the user dashboard.
|
||||
func (p *Proxy) Impersonate(w http.ResponseWriter, r *http.Request) error {
|
||||
redirectURL := urlutil.GetAbsoluteURL(r)
|
||||
redirectURL.Path = dashboardPath // redirect back to the dashboard
|
||||
signinURL := *p.authenticateSigninURL
|
||||
q := signinURL.Query()
|
||||
q.Set(urlutil.QueryRedirectURI, redirectURL.String())
|
||||
q.Set(urlutil.QueryImpersonateAction, r.FormValue(urlutil.QueryImpersonateAction))
|
||||
q.Set(urlutil.QueryImpersonateEmail, r.FormValue(urlutil.QueryImpersonateEmail))
|
||||
q.Set(urlutil.QueryImpersonateGroups, r.FormValue(urlutil.QueryImpersonateGroups))
|
||||
signinURL.RawQuery = q.Encode()
|
||||
httputil.Redirect(w, r, urlutil.NewSignedURL(p.SharedKey, &signinURL).String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Callback handles the result of a successful call to the authenticate service
|
||||
// and is responsible setting returned per-route session.
|
||||
func (p *Proxy) Callback(w http.ResponseWriter, r *http.Request) error {
|
||||
|
|
|
@ -66,57 +66,6 @@ func TestProxy_Signout(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProxy_Impersonate(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := testOptions(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
malformed bool
|
||||
options config.Options
|
||||
ctxError error
|
||||
method string
|
||||
email string
|
||||
groups string
|
||||
csrf string
|
||||
cipher encoding.MarshalUnmarshaler
|
||||
sessionStore sessions.SessionStore
|
||||
authorizer client.Authorizer
|
||||
wantStatus int
|
||||
}{
|
||||
{"good", false, opts, nil, http.MethodPost, "user@blah.com", "", "", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, client.MockAuthorize{IsAdminResponse: true}, http.StatusFound},
|
||||
{"bad session state", false, opts, errors.New("error"), http.MethodPost, "user@blah.com", "", "", &mock.Encoder{}, &mstore.Store{Session: &sessions.State{}}, client.MockAuthorize{IsAdminResponse: true}, http.StatusFound},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p, err := New(tt.options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p.encoder = tt.cipher
|
||||
p.sessionStore = tt.sessionStore
|
||||
postForm := url.Values{}
|
||||
postForm.Add("email", tt.email)
|
||||
postForm.Add("group", tt.groups)
|
||||
postForm.Set("csrf", tt.csrf)
|
||||
uri := &url.URL{Path: "/"}
|
||||
|
||||
r := httptest.NewRequest(tt.method, uri.String(), bytes.NewBufferString(postForm.Encode()))
|
||||
state, _ := tt.sessionStore.LoadSession(r)
|
||||
ctx := r.Context()
|
||||
ctx = sessions.NewContext(ctx, state, tt.ctxError)
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
|
||||
w := httptest.NewRecorder()
|
||||
httputil.HandlerFunc(p.Impersonate).ServeHTTP(w, r)
|
||||
if status := w.Code; status != tt.wantStatus {
|
||||
t.Errorf("status code: got %v want %v", status, tt.wantStatus)
|
||||
t.Errorf("\n%+v", opts)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy_SignOut(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
|
|
Loading…
Add table
Reference in a new issue