diff --git a/models/user/search.go b/models/user/search.go index 54a1ac0dfe..204f928af6 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -48,7 +48,7 @@ type SearchUserOptions struct { func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session { var cond builder.Cond if opts.Type == UserTypeIndividual { - cond = builder.In("type", UserTypeIndividual, UserTypeRemoteUser) + cond = builder.In("type", UserTypeIndividual, UserTypeBot, UserTypeRemoteUser) } else { cond = builder.Eq{"type": opts.Type} } diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index f2ae4c0943..dcc084f679 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -222,6 +222,8 @@ "admin.users.list_status_filter.not_prohibit_login": "Allow login", "admin.users.list_status_filter.is_2fa_enabled": "2FA enabled", "admin.users.list_status_filter.not_2fa_enabled": "2FA disabled", + "admin.users.is_bot": "Bot account", + "admin.users.bot.description": "Mark the account as a bot.", "admin.monitor.queues": "Queues", "admin.monitor.queue": "Queue: %s", "admin.monitor.queue.name": "Name", diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index d4a32c03a0..18c0f41d98 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -455,6 +455,7 @@ func EditUserPost(ctx *context.Context) { Visibility: optional.Some(form.Visibility), Language: optional.Some(form.Language), KeepEmailPrivate: optional.Some(form.HideEmail), + IsBot: optional.Some(form.Bot), } if err := user_service.UpdateUser(ctx, u, opts); err != nil { diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 1c5392ba83..60c11da97c 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -421,7 +421,7 @@ func Action(ctx *context.Context) { } } - if ctx.ContextUser.IsIndividual() { + if ctx.ContextUser.IsUser() { shared_user.PrepareContextForProfileBigAvatar(ctx) ctx.Data["IsHTMX"] = true ctx.HTML(http.StatusOK, tplProfileBigAvatar) diff --git a/routers/web/web.go b/routers/web/web.go index ba9ad277db..eabc4d0a04 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -943,9 +943,9 @@ func registerRoutes(m *web.Route) { reqRepoProjectsReader(ctx) } - individualPermsChecker := func(ctx *context.Context) { - // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. - if ctx.ContextUser.IsIndividual() { + userPermsChecker := func(ctx *context.Context) { + // org permissions have been checked in context.OrgAssignment(), but user permissions haven't been checked. + if ctx.ContextUser.IsUser() { switch ctx.ContextUser.Visibility { case structs.VisibleTypePrivate: if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) { @@ -1148,11 +1148,11 @@ func registerRoutes(m *web.Route) { return } }) - }, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker) + }, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), userPermsChecker) m.Group("", func() { m.Get("/code", user.CodeSearch) - }, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker) + }, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), userPermsChecker) }, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code) m.Group("/{username}/{reponame}", func() { diff --git a/services/forms/admin.go b/services/forms/admin.go index dc2cc9c909..329a28d670 100644 --- a/services/forms/admin.go +++ b/services/forms/admin.go @@ -47,6 +47,7 @@ type AdminEditUserForm struct { Active bool Admin bool Restricted bool + Bot bool AllowGitHook bool AllowImportLocal bool AllowCreateOrganization bool diff --git a/services/user/update.go b/services/user/update.go index 8b2b9cce07..3608d34dce 100644 --- a/services/user/update.go +++ b/services/user/update.go @@ -5,6 +5,7 @@ package user import ( "context" + "errors" "fmt" "forgejo.org/models" @@ -41,10 +42,11 @@ type UpdateOptions struct { RepoAdminChangeTeamAccess optional.Option[bool] EnableRepoUnitHints optional.Option[bool] KeepPronounsPrivate optional.Option[bool] + IsBot optional.Option[bool] } func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) error { - cols := make([]string, 0, 20) + cols := make([]string, 0, 21) if has, value := opts.KeepEmailPrivate.Get(); has { u.KeepEmailPrivate = value @@ -144,6 +146,17 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er u.SetLastLogin() cols = append(cols, "last_login_unix") } + if has, value := opts.IsBot.Get(); has { + if !u.IsUser() { + return errors.New("changing to bot account for non user account is not allowed") + } + if value { + u.Type = user_model.UserTypeBot + } else { + u.Type = user_model.UserTypeIndividual + } + cols = append(cols, "type") + } return user_model.UpdateUserCols(ctx, u, cols...) } diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index f18317e694..6e95b97cd5 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -135,6 +135,13 @@ {{ctx.Locale.Tr "admin.users.admin.description"}} +