Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Visible modes function from Organisation to Users too #16069

Merged
Merged
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
487d12a
First take for hiding users from explore page
sergey-dryabzhinsky Jun 4, 2021
4d194d6
Update admin user forms
sergey-dryabzhinsky Jun 4, 2021
62f2bd9
Update template for new user by admin
sergey-dryabzhinsky Jun 4, 2021
1258a6e
Add migration to list to process
sergey-dryabzhinsky Jun 4, 2021
51520a3
Format files
sergey-dryabzhinsky Jun 4, 2021
8064e02
Update swagger template
sergey-dryabzhinsky Jun 4, 2021
9d5447f
Filter users via query, not in template
sergey-dryabzhinsky Jun 5, 2021
f0b582c
Fix user edit by admin - store new field
sergey-dryabzhinsky Jun 5, 2021
266bee8
Update locale strings for user profile page
sergey-dryabzhinsky Jun 5, 2021
b419594
Hide user from public api user/userSearch by swagger, only there
sergey-dryabzhinsky Jun 6, 2021
0194b9d
Simplify userSearch limits if hide_from_explore_page set
sergey-dryabzhinsky Jun 6, 2021
1fe1a41
Check user2 too
sergey-dryabzhinsky Jun 6, 2021
9e68bb7
And add new colum for select
sergey-dryabzhinsky Jun 6, 2021
f6fbef7
Add 2 tests for api_user_search
sergey-dryabzhinsky Jun 6, 2021
71bf397
Remove useles field
sergey-dryabzhinsky Jun 6, 2021
9925724
Fix test unused/undeclared vars
sergey-dryabzhinsky Jun 6, 2021
6580f93
Fix request string formating
sergey-dryabzhinsky Jun 6, 2021
132a727
Update swapper template
sergey-dryabzhinsky Jun 6, 2021
3d74a84
Remove whitespace
sergey-dryabzhinsky Jun 6, 2021
5cb908c
Typo in fixture
sergey-dryabzhinsky Jun 6, 2021
39df5f3
Don't break old tests, use new user31
sergey-dryabzhinsky Jun 6, 2021
c38ed85
Fix unit-tests - use user31, fix fixture typo
sergey-dryabzhinsky Jun 7, 2021
5c03ff1
Pass Actor to api user search
sergey-dryabzhinsky Jun 7, 2021
0655b1f
Token auth is basic auth? Trace request from who
sergey-dryabzhinsky Jun 7, 2021
a1bae9c
Fixes for user search:
sergey-dryabzhinsky Jun 7, 2021
8b0979c
Merge branch 'main' into sergeyd-user-dont-want-exhibit-selfs
sergey-dryabzhinsky Jun 8, 2021
e1d9935
Rename migration
sergey-dryabzhinsky Jun 8, 2021
5d27211
Merge branch 'main-gitea' into sergeyd-user-dont-want-exhibit-selfs
sergey-dryabzhinsky Jun 9, 2021
f9ba4f7
Mention API searches in new locale strings
sergey-dryabzhinsky Jun 9, 2021
6bfa28e
Add note about visibility by admins
sergey-dryabzhinsky Jun 9, 2021
7066c1f
Rewrote user hiding based on Visibility field:
sergey-dryabzhinsky Jun 10, 2021
fdf1f8e
Fix lint errors
sergey-dryabzhinsky Jun 10, 2021
c80d1c8
One more step to use `Visibility` field:
sergey-dryabzhinsky Jun 10, 2021
c4c1e68
Fix test
sergey-dryabzhinsky Jun 10, 2021
9e90a73
Use edit test - new always set default visibility
sergey-dryabzhinsky Jun 10, 2021
66a0b41
One more time edit test
sergey-dryabzhinsky Jun 10, 2021
231d8ba
Next try - new user, update visibility
sergey-dryabzhinsky Jun 10, 2021
06bea36
One more try
sergey-dryabzhinsky Jun 10, 2021
aee5bd6
We can change visibility type on create - so allow it, default value …
sergey-dryabzhinsky Jun 10, 2021
cfe9ce0
Update fixture
sergey-dryabzhinsky Jun 10, 2021
efe7865
Merge branch 'main' into sergeyd-user-dont-want-exhibit-selfs
sergey-dryabzhinsky Jun 10, 2021
4fd5c86
Fix api User output?
sergey-dryabzhinsky Jun 10, 2021
16e24bd
Try to fix Visibility type conversion
sergey-dryabzhinsky Jun 10, 2021
ddc8e4c
Try to use int for visibility
sergey-dryabzhinsky Jun 11, 2021
1874740
Rewrite a little, api output for User.visibility not integer
sergey-dryabzhinsky Jun 11, 2021
ba59339
Fix swagger
sergey-dryabzhinsky Jun 11, 2021
cd70c81
Fix user test
sergey-dryabzhinsky Jun 11, 2021
96018c3
Update integrations/api_user_search_test.go
6543 Jun 15, 2021
9e051e3
Return back to string, remove zero check
sergey-dryabzhinsky Jun 16, 2021
e9e80ab
Convert visibility for API User
sergey-dryabzhinsky Jun 16, 2021
d5d20f2
Test strings now
sergey-dryabzhinsky Jun 16, 2021
d41fb82
Fixes for swagger
sergey-dryabzhinsky Jun 16, 2021
04950ca
Merge branch 'main' into sergeyd-user-dont-want-exhibit-selfs
6543 Jun 16, 2021
197f6c1
Merge branch 'master' into sergeyd-user-dont-want-exhibit-selfs
6543 Jun 16, 2021
379444f
fix
6543 Jun 16, 2021
dfe8e37
binding do not support pointer at the moment
6543 Jun 16, 2021
53ecfb5
fix TestAPIRepoTransfer
6543 Jun 16, 2021
4208b60
respect setting.Service.DefaultUserVisibilityMode
6543 Jun 16, 2021
b677e38
fix unit test :D
6543 Jun 16, 2021
994e7b9
Update new ini-option description
sergey-dryabzhinsky Jun 17, 2021
830eb28
Merge branch 'main-gitea' into sergeyd-user-dont-want-exhibit-selfs
sergey-dryabzhinsky Jun 17, 2021
87455ff
Update other ini-option description
sergey-dryabzhinsky Jun 17, 2021
06e1e73
Fix swagger template
sergey-dryabzhinsky Jun 17, 2021
a19c673
Merge branch 'main' into sergeyd-user-dont-want-exhibit-selfs
6543 Jun 18, 2021
7c95a06
Small fixes as suggested
sergey-dryabzhinsky Jun 25, 2021
f768063
Fix user_test - need to check with app-default value, fix selected va…
sergey-dryabzhinsky Jun 25, 2021
efa6ed5
Merge branch 'master' into sergeyd-user-dont-want-exhibit-selfs
6543 Jun 25, 2021
7fcb9be
User-Repos respect user Visible setting
6543 Jun 25, 2021
d04c728
Respect current User in list search
sergey-dryabzhinsky Jun 25, 2021
e4b39c2
hide user provile
6543 Jun 25, 2021
fd4f147
fix lint
6543 Jun 25, 2021
f24d3bc
Append simple visibility check to user GetInfo api
sergey-dryabzhinsky Jun 25, 2021
89255e9
Fix visibility check for api get user info:
sergey-dryabzhinsky Jun 25, 2021
79770b5
Improve the team test
zeripath Jun 26, 2021
06fbdd5
Update models/repo_permission.go
zeripath Jun 26, 2021
f258297
Merge branch 'main' into sergeyd-user-dont-want-exhibit-selfs
zeripath Jun 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -651,9 +651,15 @@ PATH =
;DEFAULT_ALLOW_CREATE_ORGANIZATION = true
;;
;; Either "public", "limited" or "private", default is "public"
;; Limited is for signed user only
;; Private is only for member of the organization
;; Public is for everyone
;; Limited is for users visible only to signed users
;; Private is for users visible only to members of their organizations
;; Public is for users visible for everyone
;DEFAULT_USER_VISIBILITY = public
;;
;; Either "public", "limited" or "private", default is "public"
;; Limited is for organizations visible only to signed users
;; Private is for organizations visible only to members of the organization
;; Public is for organizations visible to everyone
;DEFAULT_ORG_VISIBILITY = public
;;
;; Default value for DefaultOrgMemberVisible
Expand Down
1 change: 1 addition & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ relation to port exhaustion.
- `SHOW_MILESTONES_DASHBOARD_PAGE`: **true** Enable this to show the milestones dashboard page - a view of all the user's milestones
- `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created
- `AUTO_WATCH_ON_CHANGES`: **false**: Enable this to make users watch a repository after their first commit to it
- `DEFAULT_USER_VISIBILITY`: **public**: Set default visibility mode for users, either "public", "limited" or "private".
- `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private".
- `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation.
- `ALLOW_ONLY_INTERNAL_REGISTRATION`: **false** Set to true to force registration only via gitea.
Expand Down
31 changes: 31 additions & 0 deletions integrations/api_user_search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,34 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) {
}
}
}

func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) {
defer prepareTestEnv(t)()
adminUsername := "user1"
session := loginUser(t, adminUsername)
token := getTokenForLoggedInUser(t, session)
query := "user31"
req := NewRequestf(t, "GET", "/api/v1/users/search?token=%s&q=%s", token, query)
req.SetBasicAuth(token, "x-oauth-basic")
resp := session.MakeRequest(t, req, http.StatusOK)

var results SearchResults
DecodeJSON(t, resp, &results)
assert.NotEmpty(t, results.Data)
for _, user := range results.Data {
assert.Contains(t, user.UserName, query)
assert.NotEmpty(t, user.Email)
assert.EqualValues(t, "private", user.Visibility)
}
}

func TestAPIUserSearchNotLoggedInUserHidden(t *testing.T) {
defer prepareTestEnv(t)()
query := "user31"
req := NewRequestf(t, "GET", "/api/v1/users/search?q=%s", query)
resp := MakeRequest(t, req, http.StatusOK)

var results SearchResults
DecodeJSON(t, resp, &results)
assert.Empty(t, results.Data)
}
18 changes: 17 additions & 1 deletion models/fixtures/user.yml
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,6 @@
num_repos: 0
is_active: true


-
id: 30
lower_name: user30
Expand All @@ -525,3 +524,20 @@
avatar_email: user30@example.com
num_repos: 2
is_active: true

-
id: 31
lower_name: user31
name: user31
full_name: "user31"
email: user31@example.com
passwd_hash_algo: argon2
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
type: 0 # individual
salt: ZogKvWdyEx
is_admin: false
visibility: 2
avatar: avatar31
avatar_email: user31@example.com
num_repos: 0
is_active: true
16 changes: 8 additions & 8 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,22 +455,22 @@ func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
Find(&orgs)
}

// HasOrgVisible tells if the given user can see the given org
func HasOrgVisible(org, user *User) bool {
return hasOrgVisible(x, org, user)
// HasOrgOrUserVisible tells if the given user can see the given org or user
func HasOrgOrUserVisible(org, user *User) bool {
return hasOrgOrUserVisible(x, org, user)
}

func hasOrgVisible(e Engine, org, user *User) bool {
func hasOrgOrUserVisible(e Engine, orgOrUser, user *User) bool {
// Not SignedUser
if user == nil {
return org.Visibility == structs.VisibleTypePublic
return orgOrUser.Visibility == structs.VisibleTypePublic
}

if user.IsAdmin {
if user.IsAdmin || orgOrUser.ID == user.ID {
return true
}

if (org.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !org.hasMemberWithUserID(e, user.ID) {
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !orgOrUser.hasMemberWithUserID(e, user.ID) {
return false
}
return true
Expand All @@ -483,7 +483,7 @@ func HasOrgsVisible(orgs []*User, user *User) bool {
}

for _, org := range orgs {
if HasOrgVisible(org, user) {
if HasOrgOrUserVisible(org, user) {
return true
}
}
Expand Down
18 changes: 9 additions & 9 deletions models/org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,9 +586,9 @@ func TestHasOrgVisibleTypePublic(t *testing.T) {
assert.NoError(t, CreateOrganization(org, owner))
org = AssertExistsAndLoadBean(t,
&User{Name: org.Name, Type: UserTypeOrganization}).(*User)
test1 := HasOrgVisible(org, owner)
test2 := HasOrgVisible(org, user3)
test3 := HasOrgVisible(org, nil)
test1 := HasOrgOrUserVisible(org, owner)
test2 := HasOrgOrUserVisible(org, user3)
test3 := HasOrgOrUserVisible(org, nil)
assert.True(t, test1) // owner of org
assert.True(t, test2) // user not a part of org
assert.True(t, test3) // logged out user
Expand All @@ -609,9 +609,9 @@ func TestHasOrgVisibleTypeLimited(t *testing.T) {
assert.NoError(t, CreateOrganization(org, owner))
org = AssertExistsAndLoadBean(t,
&User{Name: org.Name, Type: UserTypeOrganization}).(*User)
test1 := HasOrgVisible(org, owner)
test2 := HasOrgVisible(org, user3)
test3 := HasOrgVisible(org, nil)
test1 := HasOrgOrUserVisible(org, owner)
test2 := HasOrgOrUserVisible(org, user3)
test3 := HasOrgOrUserVisible(org, nil)
assert.True(t, test1) // owner of org
assert.True(t, test2) // user not a part of org
assert.False(t, test3) // logged out user
Expand All @@ -632,9 +632,9 @@ func TestHasOrgVisibleTypePrivate(t *testing.T) {
assert.NoError(t, CreateOrganization(org, owner))
org = AssertExistsAndLoadBean(t,
&User{Name: org.Name, Type: UserTypeOrganization}).(*User)
test1 := HasOrgVisible(org, owner)
test2 := HasOrgVisible(org, user3)
test3 := HasOrgVisible(org, nil)
test1 := HasOrgOrUserVisible(org, owner)
test2 := HasOrgOrUserVisible(org, user3)
test3 := HasOrgOrUserVisible(org, nil)
assert.True(t, test1) // owner of org
assert.False(t, test2) // user not a part of org
assert.False(t, test3) // logged out user
Expand Down
3 changes: 1 addition & 2 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,8 +585,7 @@ func (repo *Repository) getReviewers(e Engine, doerID, posterID int64) ([]*User,

var users []*User

if repo.IsPrivate ||
(repo.Owner.IsOrganization() && repo.Owner.Visibility == api.VisibleTypePrivate) {
if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
// This a private repository:
// Anyone who can read the repository is a requestable reviewer
if err := e.
Expand Down
6 changes: 3 additions & 3 deletions models/repo_permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
return
}

// Prevent strangers from checking out public repo of private orginization
// Allow user if they are collaborator of a repo within a private orginization but not a member of the orginization itself
if repo.Owner.IsOrganization() && !hasOrgVisible(e, repo.Owner, user) && !isCollaborator {
// Prevent strangers from checking out public repo of private orginization/users
// Allow user if they are collaborator of a repo within a private user or a private orginization but not a member of the orginization itself
zeripath marked this conversation as resolved.
Show resolved Hide resolved
if !hasOrgOrUserVisible(e, repo.Owner, user) && !isCollaborator {
perm.AccessMode = AccessModeNone
return
}
Expand Down
127 changes: 113 additions & 14 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,84 @@ func (u *User) IsPasswordSet() bool {
return len(u.Passwd) != 0
}

// IsVisibleToUser check if viewer is able to see user profile
func (u *User) IsVisibleToUser(viewer *User) bool {

if viewer != nil && viewer.IsAdmin {
return true
}

switch u.Visibility {
case structs.VisibleTypePublic:
return true
case structs.VisibleTypeLimited:
if viewer == nil || viewer.IsRestricted {
return false
}
return true
case structs.VisibleTypePrivate:
if viewer == nil || viewer.IsRestricted {
return false
}

// If they follow - they see each over
follower := IsFollowing(u.ID, viewer.ID)
if follower {
return true
}

// Now we need to check if they in some team together

var orgIds1 []int64
var orgIds2 []int64

// First user all teams organization
if err := x.Table("team_user").
Cols("team_user.org_id").
Where("team_user.uid = ?", u.ID).
Find(&orgIds1); err != nil {
return false
}
if len(orgIds1) == 0 {
// No teams 1
return false
}

// Second user all teams organization
if err := x.Table("team_user").
Cols("team_user.org_id").
Where("team_user.uid = ?", viewer.ID).
Find(&orgIds2); err != nil {
return false
}
if len(orgIds2) == 0 {
// No teams 2
return false
}

// Intersect they organizations
var cond builder.Cond = builder.
In("id", orgIds1).
And(builder.In("id", orgIds2)).
And(builder.Eq{"type": UserTypeOrganization})
count, err := x.Table("user").
Cols("id").
Where(cond).
Count(1)
if err != nil {
return false
}
if count == 0 {
// they teams from different orgs?
return false
}

// they in some organizations together
return true
}
return false
}

// IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool {
return u.Type == UserTypeOrganization
Expand Down Expand Up @@ -796,8 +874,13 @@ func IsUsableUsername(name string) error {
return isUsableName(reservedUsernames, reservedUserPatterns, name)
}

// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
type CreateUserOverwriteOptions struct {
Visibility structs.VisibleType
}

// CreateUser creates record of a new user.
func CreateUser(u *User) (err error) {
func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
if err = IsUsableUsername(u.Name); err != nil {
return err
}
Expand Down Expand Up @@ -831,8 +914,6 @@ func CreateUser(u *User) (err error) {
return ErrEmailAlreadyUsed{u.Email}
}

u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate

u.LowerName = strings.ToLower(u.Name)
u.AvatarEmail = u.Email
if u.Rands, err = GetUserSalt(); err != nil {
Expand All @@ -841,10 +922,18 @@ func CreateUser(u *User) (err error) {
if err = u.SetPassword(u.Passwd); err != nil {
return err
}

// set system defaults
u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
u.Visibility = setting.Service.DefaultUserVisibilityMode
u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
u.MaxRepoCreation = -1
u.Theme = setting.UI.DefaultTheme
// overwrite defaults if set
if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
u.Visibility = overwriteDefault[0].Visibility
}

if _, err = sess.Insert(u); err != nil {
return err
Expand Down Expand Up @@ -1527,10 +1616,9 @@ func (opts *SearchUserOptions) toConds() builder.Cond {
cond = cond.And(keywordCond)
}

// If visibility filtered
if len(opts.Visible) > 0 {
cond = cond.And(builder.In("visibility", opts.Visible))
} else {
cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
}

if opts.Actor != nil {
Expand All @@ -1543,16 +1631,27 @@ func (opts *SearchUserOptions) toConds() builder.Cond {
exprCond = builder.Expr("org_user.org_id = \"user\".id")
}

var accessCond builder.Cond
if !opts.Actor.IsRestricted {
accessCond = builder.Or(
builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))),
builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
} else {
// restricted users only see orgs they are a member of
accessCond = builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID})))
// If Admin - they see all users!
if !opts.Actor.IsAdmin {
// Force visiblity for privacy
var accessCond builder.Cond
if !opts.Actor.IsRestricted {
accessCond = builder.Or(
builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))),
builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
} else {
// restricted users only see orgs they are a member of
accessCond = builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID})))
}
// Don't forget about self
accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID})
cond = cond.And(accessCond)
}
cond = cond.And(accessCond)

} else {
// Force visiblity for privacy
// Not logged in - only public users
cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
}

if opts.UID > 0 {
Expand Down
4 changes: 4 additions & 0 deletions modules/convert/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,14 @@ func toUser(user *models.User, signed, authed bool) *api.User {
Following: user.NumFollowing,
StarredRepos: user.NumStars,
}

result.Visibility = user.Visibility.String()

// hide primary email if API caller is anonymous or user keep email private
if signed && (!user.KeepEmailPrivate || authed) {
result.Email = user.Email
}

// only site admin will get these information and possibly user himself
if authed {
result.IsAdmin = user.IsAdmin
Expand Down
Loading