Add Visible modes function from Organisation to Users too (#16069)
You can limit or hide organisations. This pull make it also posible for users - new strings to translte - add checkbox to user profile form - add checkbox to admin user.edit form - filter explore page user search - filter api admin and public user searches - allow admins view "hidden" users - add app option DEFAULT_USER_VISIBILITY - rewrite many files to use Visibility field - check for teams intersection - fix context output - right fake 404 if not visible Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
parent
19ac575d57
commit
22a0636544
32 changed files with 440 additions and 68 deletions
|
@ -66,6 +66,7 @@ func CreateUser(ctx *context.APIContext) {
|
|||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
form := web.GetForm(ctx).(*api.CreateUserOption)
|
||||
|
||||
u := &models.User{
|
||||
Name: form.Username,
|
||||
FullName: form.FullName,
|
||||
|
@ -97,7 +98,15 @@ func CreateUser(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusBadRequest, "PasswordPwned", errors.New("PasswordPwned"))
|
||||
return
|
||||
}
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
|
||||
var overwriteDefault *models.CreateUserOverwriteOptions
|
||||
if form.Visibility != "" {
|
||||
overwriteDefault = &models.CreateUserOverwriteOptions{
|
||||
Visibility: api.VisibilityModes[form.Visibility],
|
||||
}
|
||||
}
|
||||
|
||||
if err := models.CreateUser(u, overwriteDefault); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrEmailAlreadyUsed(err) ||
|
||||
models.IsErrNameReserved(err) ||
|
||||
|
@ -209,6 +218,9 @@ func EditUser(ctx *context.APIContext) {
|
|||
if form.Active != nil {
|
||||
u.IsActive = *form.Active
|
||||
}
|
||||
if len(form.Visibility) != 0 {
|
||||
u.Visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
if form.Admin != nil {
|
||||
u.IsAdmin = *form.Admin
|
||||
}
|
||||
|
@ -395,6 +407,7 @@ func GetAllUsers(ctx *context.APIContext) {
|
|||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
|
||||
Actor: ctx.User,
|
||||
Type: models.UserTypeIndividual,
|
||||
OrderBy: models.SearchOrderByAlphabetically,
|
||||
ListOptions: listOptions,
|
||||
|
|
|
@ -225,8 +225,8 @@ func Get(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/Organization"
|
||||
|
||||
if !models.HasOrgVisible(ctx.Org.Organization, ctx.User) {
|
||||
ctx.NotFound("HasOrgVisible", nil)
|
||||
if !models.HasOrgOrUserVisible(ctx.Org.Organization, ctx.User) {
|
||||
ctx.NotFound("HasOrgOrUserVisible", nil)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToOrganization(ctx.Org.Organization))
|
||||
|
|
|
@ -375,8 +375,8 @@ func CreateOrgRepo(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !models.HasOrgVisible(org, ctx.User) {
|
||||
ctx.NotFound("HasOrgVisible", nil)
|
||||
if !models.HasOrgOrUserVisible(org, ctx.User) {
|
||||
ctx.NotFound("HasOrgOrUserVisible", nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *models.User {
|
|||
user, err := models.GetUserByName(username)
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
if redirectUserID, err := models.LookupUserRedirect(username); err == nil {
|
||||
if redirectUserID, err2 := models.LookupUserRedirect(username); err2 == nil {
|
||||
context.RedirectToUser(ctx.Context, username, redirectUserID)
|
||||
} else {
|
||||
ctx.NotFound("GetUserByName", err)
|
||||
|
|
|
@ -57,6 +57,7 @@ func Search(ctx *context.APIContext) {
|
|||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
opts := &models.SearchUserOptions{
|
||||
Actor: ctx.User,
|
||||
Keyword: strings.Trim(ctx.Query("q"), " "),
|
||||
UID: ctx.QueryInt64("uid"),
|
||||
Type: models.UserTypeIndividual,
|
||||
|
@ -102,10 +103,16 @@ func GetInfo(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/notFound"
|
||||
|
||||
u := GetUserByParams(ctx)
|
||||
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if !u.IsVisibleToUser(ctx.User) {
|
||||
// fake ErrUserNotExist error message to not leak information about existence
|
||||
ctx.NotFound("GetUserByName", models.ErrUserNotExist{Name: ctx.Params(":username")})
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.User))
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ func Organizations(ctx *context.Context) {
|
|||
ctx.Data["PageIsAdminOrganizations"] = true
|
||||
|
||||
explore.RenderUserSearch(ctx, &models.SearchUserOptions{
|
||||
Type: models.UserTypeOrganization,
|
||||
Actor: ctx.User,
|
||||
Type: models.UserTypeOrganization,
|
||||
ListOptions: models.ListOptions{
|
||||
PageSize: setting.UI.Admin.OrgPagingNum,
|
||||
},
|
||||
|
|
|
@ -37,7 +37,8 @@ func Users(ctx *context.Context) {
|
|||
ctx.Data["PageIsAdminUsers"] = true
|
||||
|
||||
explore.RenderUserSearch(ctx, &models.SearchUserOptions{
|
||||
Type: models.UserTypeIndividual,
|
||||
Actor: ctx.User,
|
||||
Type: models.UserTypeIndividual,
|
||||
ListOptions: models.ListOptions{
|
||||
PageSize: setting.UI.Admin.UserPagingNum,
|
||||
},
|
||||
|
@ -50,6 +51,7 @@ func NewUser(ctx *context.Context) {
|
|||
ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminUsers"] = true
|
||||
ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
|
||||
|
||||
ctx.Data["login_type"] = "0-0"
|
||||
|
||||
|
@ -70,6 +72,7 @@ func NewUserPost(ctx *context.Context) {
|
|||
ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminUsers"] = true
|
||||
ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
|
||||
|
||||
sources, err := models.LoginSources()
|
||||
if err != nil {
|
||||
|
@ -126,7 +129,8 @@ func NewUserPost(ctx *context.Context) {
|
|||
}
|
||||
u.MustChangePassword = form.MustChangePassword
|
||||
}
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
|
||||
if err := models.CreateUser(u, &models.CreateUserOverwriteOptions{Visibility: form.Visibility}); err != nil {
|
||||
switch {
|
||||
case models.IsErrUserAlreadyExist(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
|
@ -312,6 +316,8 @@ func EditUserPost(ctx *context.Context) {
|
|||
u.AllowImportLocal = form.AllowImportLocal
|
||||
u.AllowCreateOrganization = form.AllowCreateOrganization
|
||||
|
||||
u.Visibility = form.Visibility
|
||||
|
||||
// skip self Prohibit Login
|
||||
if ctx.User.ID == u.ID {
|
||||
u.ProhibitLogin = false
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
|
@ -121,3 +123,82 @@ func TestNewUserPost_InvalidEmail(t *testing.T) {
|
|||
|
||||
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
|
||||
}
|
||||
|
||||
func TestNewUserPost_VisiblityDefaultPublic(t *testing.T) {
|
||||
|
||||
models.PrepareTestEnv(t)
|
||||
ctx := test.MockContext(t, "admin/users/new")
|
||||
|
||||
u := models.AssertExistsAndLoadBean(t, &models.User{
|
||||
IsAdmin: true,
|
||||
ID: 2,
|
||||
}).(*models.User)
|
||||
|
||||
ctx.User = u
|
||||
|
||||
username := "gitea"
|
||||
email := "gitea@gitea.io"
|
||||
|
||||
form := forms.AdminCreateUserForm{
|
||||
LoginType: "local",
|
||||
LoginName: "local",
|
||||
UserName: username,
|
||||
Email: email,
|
||||
Password: "abc123ABC!=$",
|
||||
SendNotify: false,
|
||||
MustChangePassword: false,
|
||||
}
|
||||
|
||||
web.SetForm(ctx, &form)
|
||||
NewUserPost(ctx)
|
||||
|
||||
assert.NotEmpty(t, ctx.Flash.SuccessMsg)
|
||||
|
||||
u, err := models.GetUserByName(username)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, username, u.Name)
|
||||
assert.Equal(t, email, u.Email)
|
||||
// As default user visibility
|
||||
assert.Equal(t, setting.Service.DefaultUserVisibilityMode, u.Visibility)
|
||||
}
|
||||
|
||||
func TestNewUserPost_VisibilityPrivate(t *testing.T) {
|
||||
|
||||
models.PrepareTestEnv(t)
|
||||
ctx := test.MockContext(t, "admin/users/new")
|
||||
|
||||
u := models.AssertExistsAndLoadBean(t, &models.User{
|
||||
IsAdmin: true,
|
||||
ID: 2,
|
||||
}).(*models.User)
|
||||
|
||||
ctx.User = u
|
||||
|
||||
username := "gitea"
|
||||
email := "gitea@gitea.io"
|
||||
|
||||
form := forms.AdminCreateUserForm{
|
||||
LoginType: "local",
|
||||
LoginName: "local",
|
||||
UserName: username,
|
||||
Email: email,
|
||||
Password: "abc123ABC!=$",
|
||||
SendNotify: false,
|
||||
MustChangePassword: false,
|
||||
Visibility: api.VisibleTypePrivate,
|
||||
}
|
||||
|
||||
web.SetForm(ctx, &form)
|
||||
NewUserPost(ctx)
|
||||
|
||||
assert.NotEmpty(t, ctx.Flash.SuccessMsg)
|
||||
|
||||
u, err := models.GetUserByName(username)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, username, u.Name)
|
||||
assert.Equal(t, email, u.Email)
|
||||
// As default user visibility
|
||||
assert.True(t, u.Visibility.IsPrivate())
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ func Home(ctx *context.Context) {
|
|||
|
||||
org := ctx.Org.Organization
|
||||
|
||||
if !models.HasOrgVisible(org, ctx.User) {
|
||||
ctx.NotFound("HasOrgVisible", nil)
|
||||
if !models.HasOrgOrUserVisible(org, ctx.User) {
|
||||
ctx.NotFound("HasOrgOrUserVisible", nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,17 @@ func Profile(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
org.Home(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// check view permissions
|
||||
if !ctxUser.IsVisibleToUser(ctx.User) {
|
||||
ctx.NotFound("user", fmt.Errorf(uname))
|
||||
return
|
||||
}
|
||||
|
||||
// Show SSH keys.
|
||||
if isShowKeys {
|
||||
ShowSSHKeys(ctx, ctxUser.ID)
|
||||
|
@ -87,11 +98,6 @@ func Profile(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
org.Home(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// Show OpenID URIs
|
||||
openIDs, err := models.GetUserOpenIDs(ctxUser.ID)
|
||||
if err != nil {
|
||||
|
|
|
@ -114,6 +114,7 @@ func ProfilePost(ctx *context.Context) {
|
|||
}
|
||||
ctx.User.Description = form.Description
|
||||
ctx.User.KeepActivityPrivate = form.KeepActivityPrivate
|
||||
ctx.User.Visibility = form.Visibility
|
||||
if err := models.UpdateUserSetting(ctx.User); err != nil {
|
||||
if _, ok := err.(models.ErrEmailAlreadyUsed); ok {
|
||||
ctx.Flash.Error(ctx.Tr("form.email_been_used"))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue