diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index bc376bcc435..47e946a37fe 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -3052,6 +3052,8 @@ "admin.users.new_success": "The user account \"%s\" has been created.", "admin.users.edit": "Edit", "admin.users.impersonate": "Impersonate", + "admin.users.impersonate_stop": "Stop impersonating", + "admin.users.impersonating_notice": "You are impersonating %s. Actions you take are performed as this user.", "admin.users.auth_source": "Authentication Source", "admin.users.local": "Local", "admin.users.auth_login_name": "Authentication Sign-In Name", diff --git a/routers/web/home.go b/routers/web/home.go index 7f1491a41d4..38496aaa28a 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -38,7 +38,7 @@ func Home(ctx *context.Context) { log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if ctx.Doer.MustChangePassword { + } else if doerMustChangePassword(ctx) { ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) diff --git a/routers/web/web.go b/routers/web/web.go index 59f85166470..211c03b2c1f 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -166,6 +166,11 @@ func newWebAuthMiddleware() *AuthMiddleware { return webAuth } +func doerMustChangePassword(ctx *context.Context) bool { + // an impersonating admin must not be forced to set the impersonated user's password + return ctx.Doer != nil && ctx.Doer.MustChangePassword && !ctx.DoerIsImpersonated() +} + // verifyAuthWithOptions checks authentication according to options func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Context) { crossOriginProtection := http.NewCrossOriginProtection() @@ -185,7 +190,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont return } - if ctx.Doer.MustChangePassword { + if doerMustChangePassword(ctx) { if ctx.Req.URL.Path != "/user/settings/change_password" { if strings.HasPrefix(ctx.Req.UserAgent(), "git") { ctx.HTTPError(http.StatusUnauthorized, ctx.Locale.TrString("auth.must_change_password")) diff --git a/services/context/context.go b/services/context/context.go index 8bbe0795edf..6f4cf63cfa7 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -228,6 +228,11 @@ func (ctx *Context) DoerNeedTwoFactorAuth() bool { return ctx.Session.Get(session.KeyUserHasTwoFactorAuth) == false } +// DoerIsImpersonated returns true if the current session is an admin impersonating the doer +func (ctx *Context) DoerIsImpersonated() bool { + return ctx.Session.Get(session.KeyImpersonatorData) != nil +} + // HasError returns true if error occurs in form validation. // Attention: this function changes ctx.Data and ctx.Flash // If HasError is called, then before Redirect, the error message should be stored by ctx.Flash.Error(ctx.GetErrMsg()) again. diff --git a/services/context/context_template.go b/services/context/context_template.go index b1c213ca9e1..b4775171eb9 100644 --- a/services/context/context_template.go +++ b/services/context/context_template.go @@ -12,6 +12,7 @@ import ( "strings" "time" + user_model "gitea.dev/models/user" "gitea.dev/modules/htmlutil" "gitea.dev/modules/httplib" "gitea.dev/modules/public" @@ -66,6 +67,14 @@ func (c TemplateContext) CurrentWebTheme() *webtheme.ThemeMetaInfo { return webtheme.GuaranteeGetThemeMetaInfo(themeName) } +func (c TemplateContext) ImpersonatedUser() *user_model.User { + webCtx := GetWebContext(c) + if webCtx == nil || webCtx.Doer == nil || !webCtx.DoerIsImpersonated() { + return nil + } + return webCtx.Doer +} + func (c TemplateContext) CurrentWebBanner() *setting.WebBannerType { // Using revision as a simple approach to determine if the banner has been changed after the user dismissed it. // There could be some false-positives because revision can be changed even if the banner isn't. diff --git a/templates/admin/config_settings/instance.tmpl b/templates/admin/config_settings/instance.tmpl index da28fffddb4..c6203aca4a3 100644 --- a/templates/admin/config_settings/instance.tmpl +++ b/templates/admin/config_settings/instance.tmpl @@ -37,7 +37,7 @@ {{template "shared/combomarkdowneditor" (dict - "ContainerClasses" "web-banner-content-editor" + "ContainerClasses" "site-banner-content-editor" "TextareaName" (print $cfgKey ".ContentMessage") "TextareaContent" $banner.ContentMessage "TextareaPlaceholder" (ctx.Locale.Tr "admin.config.instance_web_banner.message_placeholder") diff --git a/templates/base/head_banner.tmpl b/templates/base/head_banner.tmpl index d237161622a..d8b95cd8daa 100644 --- a/templates/base/head_banner.tmpl +++ b/templates/base/head_banner.tmpl @@ -1,10 +1,10 @@ {{$banner := ctx.CurrentWebBanner}} {{if $banner}} -
+{{template "base/head_impersonate_banner"}} {{template "base/head_banner"}} diff --git a/templates/devtest/severity-colors.tmpl b/templates/devtest/severity-colors.tmpl index 43a51614655..efd7369301a 100644 --- a/templates/devtest/severity-colors.tmpl +++ b/templates/devtest/severity-colors.tmpl @@ -59,7 +59,7 @@