From ce8991c0c61163a9bca47d2d12eb98c00ee80e75 Mon Sep 17 00:00:00 2001 From: Denys Lafazan Date: Tue, 18 Jul 2023 15:54:41 +0300 Subject: [PATCH 1/2] get impersonate token --- web/api/login.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++ web/api/routes.go | 2 ++ 2 files changed, 90 insertions(+) diff --git a/web/api/login.go b/web/api/login.go index efc7e875..7848781e 100644 --- a/web/api/login.go +++ b/web/api/login.go @@ -383,3 +383,91 @@ func (ar *Router) loginFlow(app model.AppData, user model.User, requestedScopes result.User = user return result, nil } + +type impersonateData struct { + login + UserID string `json:"user_id" validate:"required"` + Scopes []string `json:"scopes,omitempty"` +} + +// GetImpersonateToken returns a token that allows to impersonate a user. +func (ar *Router) GetImpersonateToken() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + locale := r.Header.Get("Accept-Language") + + ld := impersonateData{} + if ar.MustParseJSON(w, r, &ld) != nil { + return + } + + if err := ld.validate(); err != nil { + ar.Error(w, locale, http.StatusBadRequest, l.ErrorAPIRequestBodyInvalidError, err) + return + } + + if err := ar.checkSupportedWays(ld.login); err != nil { + ar.Error(w, locale, http.StatusBadRequest, l.APIAPPUsernameLoginNotSupported) + return + } + + var err error + var user model.User + + if len(ld.UserID) > 0 { + user, err = ar.server.Storages().User.UserByID(ld.UserID) + if err != nil { + ar.Error(w, locale, http.StatusBadRequest, l.ErrorAPIRequestIncorrectLoginOrPassword) + return + } + } else { + ar.Error(w, locale, http.StatusBadRequest, l.ErrorAPIRequestBodyInvalidError) + return + } + + app := middleware.AppFromContext(r.Context()) + if len(app.ID) == 0 { + ar.Error(w, locale, http.StatusBadRequest, l.ErrorAPIAPPNoAPPInContext) + return + } + + // Authorize user if the app requires authorization. + azi := authorization.AuthzInfo{ + App: app, + UserRole: user.AccessRole, + ResourceURI: r.RequestURI, + Method: r.Method, + } + if err := ar.Authorizer.Authorize(azi); err != nil { + ar.Error(w, locale, http.StatusForbidden, l.APIAccessDenied) + return + } + + impersonateToken, err := ar.getImpersonateAccessToken(user, ld.Scopes, app) + if err != nil { + ar.Error(w, locale, http.StatusInternalServerError, l.ErrorAPILoginError, err) + return + } + + ar.ServeJSON(w, locale, http.StatusOK, impersonateToken) + } +} + +// getImpersonateAccessToken creates and returns access token for a user. +func (ar *Router) getImpersonateAccessToken(user model.User, scopes []string, app model.AppData) (string, error) { + tokenPayload, err := ar.getTokenPayloadForApp(app, user.ID) + if err != nil { + return "", err + } + + token, err := ar.server.Services().Token.NewAccessToken(user, scopes, app, false, tokenPayload) + if err != nil { + return "", err + } + + accessTokenString, err := ar.server.Services().Token.String(token) + if err != nil { + return "", err + } + + return accessTokenString, nil +} diff --git a/web/api/routes.go b/web/api/routes.go index 9e6ddbab..9cf5e9c6 100644 --- a/web/api/routes.go +++ b/web/api/routes.go @@ -100,6 +100,8 @@ func (ar *Router) buildAuthRoutes(middlewares *negroni.Negroni) http.Handler { auth.Path("/invite").Handler( ar.Token(model.TokenTypeAccess, nil)(ar.RequestInviteLink()), ).Methods(http.MethodPost) + auth.Path("/impersonate_token").Handler( + ar.GetImpersonateToken()).Methods(http.MethodPost) auth.Path("/tfa/enable").Handler( ar.Token(model.TokenTypeAccess, nil)(ar.EnableTFA()), From b05312e62995850436d62631af4965d346558186 Mon Sep 17 00:00:00 2001 From: Denys Lafazan Date: Wed, 19 Jul 2023 09:40:49 +0300 Subject: [PATCH 2/2] remove unnecesary 'login' field --- web/api/login.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/web/api/login.go b/web/api/login.go index 7848781e..b60632a1 100644 --- a/web/api/login.go +++ b/web/api/login.go @@ -385,7 +385,6 @@ func (ar *Router) loginFlow(app model.AppData, user model.User, requestedScopes } type impersonateData struct { - login UserID string `json:"user_id" validate:"required"` Scopes []string `json:"scopes,omitempty"` } @@ -400,16 +399,6 @@ func (ar *Router) GetImpersonateToken() http.HandlerFunc { return } - if err := ld.validate(); err != nil { - ar.Error(w, locale, http.StatusBadRequest, l.ErrorAPIRequestBodyInvalidError, err) - return - } - - if err := ar.checkSupportedWays(ld.login); err != nil { - ar.Error(w, locale, http.StatusBadRequest, l.APIAPPUsernameLoginNotSupported) - return - } - var err error var user model.User @@ -437,6 +426,7 @@ func (ar *Router) GetImpersonateToken() http.HandlerFunc { ResourceURI: r.RequestURI, Method: r.Method, } + if err := ar.Authorizer.Authorize(azi); err != nil { ar.Error(w, locale, http.StatusForbidden, l.APIAccessDenied) return