mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-08 22:13:26 +09:00
feat: admin impersonates a user (#38614)
* fix #3631 * fix #21599 by the way, refactored the "profile avatar card" to simplify the code.
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"gitea.dev/modules/graceful"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/session"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/timeutil"
|
||||
@@ -502,15 +503,10 @@ func SubmitInstall(ctx *context.Context) {
|
||||
ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day)
|
||||
|
||||
// Auto-login for admin
|
||||
if err = ctx.Session.Set("uid", u.ID); err != nil {
|
||||
if err = ctx.Session.Set(session.KeyUID, u.ID); err != nil {
|
||||
ctx.RenderWithErrDeprecated(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
|
||||
return
|
||||
}
|
||||
if err = ctx.Session.Set("uname", u.Name); err != nil {
|
||||
ctx.RenderWithErrDeprecated(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
|
||||
return
|
||||
}
|
||||
|
||||
if err = ctx.Session.Release(); err != nil {
|
||||
ctx.RenderWithErrDeprecated(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
|
||||
return
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"gitea.dev/modules/web"
|
||||
"gitea.dev/routers/web/explore"
|
||||
user_setting "gitea.dev/routers/web/user/setting"
|
||||
auth_service "gitea.dev/services/auth"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/forms"
|
||||
"gitea.dev/services/mailer"
|
||||
@@ -460,6 +461,20 @@ func EditUserPost(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
|
||||
}
|
||||
|
||||
func ImpersonateUser(ctx *context.Context) {
|
||||
u, err := user_model.GetUserByID(ctx, ctx.PathParamInt64("userid"))
|
||||
if err != nil {
|
||||
ctx.JSONError("unable to get user")
|
||||
return
|
||||
}
|
||||
err = auth_service.ImpersonateUser(ctx.Session, u)
|
||||
if err != nil {
|
||||
ctx.ServerError("unable to impersonate user", err)
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/user/settings")
|
||||
}
|
||||
|
||||
// DeleteUser response for deleting a user
|
||||
func DeleteUser(ctx *context.Context) {
|
||||
u, err := user_model.GetUserByID(ctx, ctx.PathParamInt64("userid"))
|
||||
|
||||
+16
-25
@@ -118,9 +118,8 @@ func autoSignIn(ctx *context.Context) (bool, error) {
|
||||
|
||||
ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day)
|
||||
|
||||
if err := regenerateSession(ctx, nil, map[string]any{
|
||||
if err := regenerateSession(ctx, map[string]any{
|
||||
session.KeyUID: u.ID,
|
||||
session.KeyUname: u.Name,
|
||||
session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
|
||||
}); err != nil {
|
||||
return false, fmt.Errorf("unable to updateSession: %w", err)
|
||||
@@ -357,7 +356,7 @@ func SignInPost(ctx *context.Context) {
|
||||
// User will need to use WebAuthn, save data
|
||||
updates["totpEnrolled"] = u.ID
|
||||
}
|
||||
if err := regenerateSession(ctx, nil, updates); err != nil {
|
||||
if err := regenerateSession(ctx, updates); err != nil {
|
||||
ctx.ServerError("UserSignIn: Unable to update session", err)
|
||||
return
|
||||
}
|
||||
@@ -398,19 +397,9 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember bool) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := regenerateSession(ctx, []string{
|
||||
// Delete the openid, 2fa and link_account data
|
||||
"openid_verified_uri",
|
||||
"openid_signin_remember",
|
||||
"openid_determined_email",
|
||||
"openid_determined_username",
|
||||
"twofaUid",
|
||||
"twofaRemember",
|
||||
"linkAccount",
|
||||
"linkAccountData",
|
||||
}, map[string]any{
|
||||
auth_service.ClearSessionKeysForSignIn(ctx.Session)
|
||||
if err := regenerateSession(ctx, map[string]any{
|
||||
session.KeyUID: u.ID,
|
||||
session.KeyUname: u.Name,
|
||||
session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
|
||||
}); err != nil {
|
||||
ctx.ServerError("RegenerateSession", err)
|
||||
@@ -477,6 +466,16 @@ func SignOut(ctx *context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
exitedImpersonated, err := auth_service.ExitImpersonatedUser(ctx.Session)
|
||||
if err != nil {
|
||||
ctx.ServerError("ExitImpersonatedUser", err)
|
||||
return
|
||||
}
|
||||
if exitedImpersonated {
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin")
|
||||
return
|
||||
}
|
||||
|
||||
// prepare the sign-out URL before destroying the session
|
||||
redirectTo := buildSignOutRedirectURL(ctx)
|
||||
HandleSignOut(ctx)
|
||||
@@ -884,10 +883,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
|
||||
|
||||
log.Trace("User activated: %s", user.Name)
|
||||
|
||||
if err := regenerateSession(ctx, nil, map[string]any{
|
||||
"uid": user.ID,
|
||||
"uname": user.Name,
|
||||
}); err != nil {
|
||||
if err := regenerateSession(ctx, map[string]any{session.KeyUID: user.ID}); err != nil {
|
||||
log.Error("Unable to regenerate session for user: %-v with email: %s: %v", user, user.Email, err)
|
||||
ctx.ServerError("ActivateUserEmail", err)
|
||||
return
|
||||
@@ -936,17 +932,12 @@ func ActivateEmail(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||
}
|
||||
|
||||
func regenerateSession(ctx *context.Context, deletes []string, updates map[string]any) error {
|
||||
func regenerateSession(ctx *context.Context, updates map[string]any) error {
|
||||
if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
|
||||
return fmt.Errorf("regenerate session: %w", err)
|
||||
}
|
||||
sess := ctx.Session
|
||||
sessID := sess.ID()
|
||||
for _, k := range deletes {
|
||||
if err := sess.Delete(k); err != nil {
|
||||
return fmt.Errorf("delete %v in session[%s]: %w", k, sessID, err)
|
||||
}
|
||||
}
|
||||
for k, v := range updates {
|
||||
if err := sess.Set(k, v); err != nil {
|
||||
return fmt.Errorf("set %v in session[%s]: %w", k, sessID, err)
|
||||
|
||||
@@ -169,7 +169,7 @@ func oauth2LinkAccount(ctx *context.Context, u *user_model.User, linkAccountData
|
||||
return
|
||||
}
|
||||
|
||||
if err := regenerateSession(ctx, nil, map[string]any{
|
||||
if err := regenerateSession(ctx, map[string]any{
|
||||
// User needs to use 2FA, save data and redirect to 2FA page.
|
||||
"twofaUid": u.ID,
|
||||
"twofaRemember": remember,
|
||||
|
||||
@@ -428,9 +428,8 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m
|
||||
return
|
||||
}
|
||||
|
||||
if err := regenerateSession(ctx, nil, map[string]any{
|
||||
if err := regenerateSession(ctx, map[string]any{
|
||||
session.KeyUID: u.ID,
|
||||
session.KeyUname: u.Name,
|
||||
session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
|
||||
}); err != nil {
|
||||
ctx.ServerError("updateSession", err)
|
||||
@@ -453,7 +452,7 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m
|
||||
}
|
||||
}
|
||||
|
||||
if err := regenerateSession(ctx, nil, map[string]any{
|
||||
if err := regenerateSession(ctx, map[string]any{
|
||||
// User needs to use 2FA, save data and redirect to 2FA page.
|
||||
"twofaUid": u.ID,
|
||||
"twofaRemember": false,
|
||||
|
||||
@@ -213,7 +213,7 @@ func signInOpenIDVerify(ctx *context.Context) {
|
||||
if u != nil {
|
||||
nickname = u.LowerName
|
||||
}
|
||||
if err := regenerateSession(ctx, nil, map[string]any{
|
||||
if err := regenerateSession(ctx, map[string]any{
|
||||
"openid_verified_uri": id,
|
||||
"openid_determined_email": email,
|
||||
"openid_determined_username": nickname,
|
||||
|
||||
+3
-1
@@ -16,6 +16,7 @@ import (
|
||||
"gitea.dev/modules/metrics"
|
||||
"gitea.dev/modules/public"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/session"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/storage"
|
||||
"gitea.dev/modules/structs"
|
||||
@@ -159,7 +160,7 @@ func newWebAuthMiddleware() *AuthMiddleware {
|
||||
ctx.IsBasicAuth = ar.IsBasicAuth
|
||||
if ctx.Doer == nil {
|
||||
// ensure the session uid is deleted
|
||||
_ = ctx.Session.Delete("uid")
|
||||
_ = ctx.Session.Delete(session.KeyUID)
|
||||
}
|
||||
}
|
||||
return webAuth
|
||||
@@ -795,6 +796,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
||||
m.Combo("/new").Get(admin.NewUser).Post(web.Bind(forms.AdminCreateUserForm{}), admin.NewUserPost)
|
||||
m.Get("/{userid}", admin.ViewUser)
|
||||
m.Combo("/{userid}/edit").Get(admin.EditUser).Post(web.Bind(forms.AdminEditUserForm{}), admin.EditUserPost)
|
||||
m.Post("/{userid}/impersonate", admin.ImpersonateUser)
|
||||
m.Post("/{userid}/delete", admin.DeleteUser)
|
||||
m.Post("/{userid}/avatar", web.Bind(forms.AvatarForm{}), admin.AvatarPost)
|
||||
m.Post("/{userid}/avatar/delete", admin.DeleteAvatar)
|
||||
|
||||
Reference in New Issue
Block a user