From d718e3f68c0fad439bad2303f95ab748caeda3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20=C5=BDen=C4=8D=C3=A1k?= Date: Wed, 29 Nov 2023 13:09:09 +0100 Subject: [PATCH 1/5] Allow admins and org owners to change org member public status --- routers/api/v1/org/member.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 422b7cecfee14..7c803a927a29e 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -236,7 +237,7 @@ func PublicizeMember(ctx *context.APIContext) { if ctx.Written() { return } - if userToPublicize.ID != ctx.Doer.ID { + if userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin && !organization.IsUserOrgOwner(ctx, []*user_model.User{ctx.Doer}, ctx.Org.Organization.ID)[ctx.Doer.ID] { ctx.Error(http.StatusForbidden, "", "Cannot publicize another member") return } @@ -278,7 +279,7 @@ func ConcealMember(ctx *context.APIContext) { if ctx.Written() { return } - if userToConceal.ID != ctx.Doer.ID { + if userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin && !organization.IsUserOrgOwner(ctx, []*user_model.User{ctx.Doer}, ctx.Org.Organization.ID)[ctx.Doer.ID] { ctx.Error(http.StatusForbidden, "", "Cannot conceal another member") return } From a352455b81cf94cf8d2dfa4729426a0edcbc8f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20=C5=BDen=C4=8D=C3=A1k?= Date: Thu, 1 Feb 2024 14:45:01 +0100 Subject: [PATCH 2/5] Use ctx.Org.Organization.IsOwnedBy --- routers/api/v1/org/member.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 7c803a927a29e..437532ad5cb09 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -9,7 +9,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/organization" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -237,7 +236,16 @@ func PublicizeMember(ctx *context.APIContext) { if ctx.Written() { return } - if userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin && !organization.IsUserOrgOwner(ctx, []*user_model.User{ctx.Doer}, ctx.Org.Organization.ID)[ctx.Doer.ID] { + allowed := userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin + if !allowed { + isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) + return + } + allowed = isOwner + } + if !allowed { ctx.Error(http.StatusForbidden, "", "Cannot publicize another member") return } @@ -279,7 +287,16 @@ func ConcealMember(ctx *context.APIContext) { if ctx.Written() { return } - if userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin && !organization.IsUserOrgOwner(ctx, []*user_model.User{ctx.Doer}, ctx.Org.Organization.ID)[ctx.Doer.ID] { + allowed := userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin + if !allowed { + isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) + return + } + allowed = isOwner + } + if !allowed { ctx.Error(http.StatusForbidden, "", "Cannot conceal another member") return } From 754ce8ee6164a9a364f58d48f5306252eb0049fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20=C5=BDen=C4=8D=C3=A1k?= Date: Fri, 2 Feb 2024 11:04:59 +0100 Subject: [PATCH 3/5] Simplify, fix logic error --- routers/api/v1/org/member.go | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 437532ad5cb09..f9a7445bcf015 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -236,18 +236,16 @@ func PublicizeMember(ctx *context.APIContext) { if ctx.Written() { return } - allowed := userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin - if !allowed { + if userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) return } - allowed = isOwner - } - if !allowed { - ctx.Error(http.StatusForbidden, "", "Cannot publicize another member") - return + if !isOwner { + ctx.Error(http.StatusForbidden, "", "Cannot publicize another member") + return + } } err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToPublicize.ID, true) if err != nil { @@ -287,18 +285,16 @@ func ConcealMember(ctx *context.APIContext) { if ctx.Written() { return } - allowed := userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin - if !allowed { + if userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) return } - allowed = isOwner - } - if !allowed { - ctx.Error(http.StatusForbidden, "", "Cannot conceal another member") - return + if !isOwner { + ctx.Error(http.StatusForbidden, "", "Cannot conceal another member") + return + } } err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToConceal.ID, false) if err != nil { From 046151ef9070846cae0fbb03d0dd7d6e6282b18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20=C5=BDen=C4=8D=C3=A1k?= Date: Fri, 2 Feb 2024 11:07:19 +0100 Subject: [PATCH 4/5] Fix error string --- routers/api/v1/org/member.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index f9a7445bcf015..f69c6614211ec 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -239,7 +239,7 @@ func PublicizeMember(ctx *context.APIContext) { if userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) + ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) return } if !isOwner { @@ -288,7 +288,7 @@ func ConcealMember(ctx *context.APIContext) { if userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) + ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) return } if !isOwner { From 6e9b8a9f7e3150343cc6ba18ab9197fe78c3d46c Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 13 Apr 2025 15:42:47 +0800 Subject: [PATCH 5/5] fix --- routers/api/v1/org/member.go | 41 ++--- tests/integration/api_org_test.go | 228 +++++++++++++----------- tests/integration/api_team_user_test.go | 52 +++--- 3 files changed, 173 insertions(+), 148 deletions(-) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 1890e98915736..a1875a7886a72 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -8,6 +8,7 @@ import ( "net/url" "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" @@ -210,6 +211,20 @@ func IsPublicMember(ctx *context.APIContext) { } } +func checkCanChangeOrgUserStatus(ctx *context.APIContext, targetUser *user_model.User) { + // allow user themselves to change their status, and allow admins to change any user + if targetUser.ID == ctx.Doer.ID || ctx.Doer.IsAdmin { + return + } + // allow org owners to change status of members + isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) + if err != nil { + ctx.APIError(http.StatusInternalServerError, err) + } else if !isOwner { + ctx.APIError(http.StatusForbidden, "Cannot change member visibility") + } +} + // PublicizeMember make a member's membership public func PublicizeMember(ctx *context.APIContext) { // swagger:operation PUT /orgs/{org}/public_members/{username} organization orgPublicizeMember @@ -240,16 +255,9 @@ func PublicizeMember(ctx *context.APIContext) { if ctx.Written() { return } - if userToPublicize.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { - isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) - if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) - return - } - if !isOwner { - ctx.Error(http.StatusForbidden, "", "Cannot publicize another member") - return - } + checkCanChangeOrgUserStatus(ctx, userToPublicize) + if ctx.Written() { + return } err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToPublicize.ID, true) if err != nil { @@ -289,16 +297,9 @@ func ConcealMember(ctx *context.APIContext) { if ctx.Written() { return } - if userToConceal.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { - isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) - if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) - return - } - if !isOwner { - ctx.Error(http.StatusForbidden, "", "Cannot conceal another member") - return - } + checkCanChangeOrgUserStatus(ctx, userToConceal) + if ctx.Written() { + return } err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToConceal.ID, false) if err != nil { diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go index 46b96dc7c137a..6577bd1684c04 100644 --- a/tests/integration/api_org_test.go +++ b/tests/integration/api_org_test.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAPIOrgCreateRename(t *testing.T) { @@ -110,121 +111,142 @@ func TestAPIOrgCreateRename(t *testing.T) { }) } -func TestAPIOrgEdit(t *testing.T) { +func TestAPIOrgGeneral(t *testing.T) { defer tests.PrepareTestEnv(t)() - session := loginUser(t, "user1") - - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization) - org := api.EditOrgOption{ - FullName: "Org3 organization new full name", - Description: "A new description", - Website: "https://try.gitea.io/new", - Location: "Beijing", - Visibility: "private", - } - req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) + user1Session := loginUser(t, "user1") + user1Token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteOrganization) + + t.Run("OrgGetAll", func(t *testing.T) { + // accessing with a token will return all orgs + req := NewRequest(t, "GET", "/api/v1/orgs").AddTokenAuth(user1Token) + resp := MakeRequest(t, req, http.StatusOK) + var apiOrgList []*api.Organization + + DecodeJSON(t, resp, &apiOrgList) + assert.Len(t, apiOrgList, 13) + assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName) + assert.Equal(t, "limited", apiOrgList[1].Visibility) + + // accessing without a token will return only public orgs + req = NewRequest(t, "GET", "/api/v1/orgs") + resp = MakeRequest(t, req, http.StatusOK) - var apiOrg api.Organization - DecodeJSON(t, resp, &apiOrg) + DecodeJSON(t, resp, &apiOrgList) + assert.Len(t, apiOrgList, 9) + assert.Equal(t, "org 17", apiOrgList[0].FullName) + assert.Equal(t, "public", apiOrgList[0].Visibility) + }) - assert.Equal(t, "org3", apiOrg.Name) - assert.Equal(t, org.FullName, apiOrg.FullName) - assert.Equal(t, org.Description, apiOrg.Description) - assert.Equal(t, org.Website, apiOrg.Website) - assert.Equal(t, org.Location, apiOrg.Location) - assert.Equal(t, org.Visibility, apiOrg.Visibility) -} + t.Run("OrgEdit", func(t *testing.T) { + org := api.EditOrgOption{ + FullName: "Org3 organization new full name", + Description: "A new description", + Website: "https://try.gitea.io/new", + Location: "Beijing", + Visibility: "private", + } + req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiOrg api.Organization + DecodeJSON(t, resp, &apiOrg) + + assert.Equal(t, "org3", apiOrg.Name) + assert.Equal(t, org.FullName, apiOrg.FullName) + assert.Equal(t, org.Description, apiOrg.Description) + assert.Equal(t, org.Website, apiOrg.Website) + assert.Equal(t, org.Location, apiOrg.Location) + assert.Equal(t, org.Visibility, apiOrg.Visibility) + }) -func TestAPIOrgEditBadVisibility(t *testing.T) { - defer tests.PrepareTestEnv(t)() - session := loginUser(t, "user1") - - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization) - org := api.EditOrgOption{ - FullName: "Org3 organization new full name", - Description: "A new description", - Website: "https://try.gitea.io/new", - Location: "Beijing", - Visibility: "badvisibility", - } - req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusUnprocessableEntity) -} + t.Run("OrgEditBadVisibility", func(t *testing.T) { + org := api.EditOrgOption{ + FullName: "Org3 organization new full name", + Description: "A new description", + Website: "https://try.gitea.io/new", + Location: "Beijing", + Visibility: "badvisibility", + } + req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token) + MakeRequest(t, req, http.StatusUnprocessableEntity) + }) -func TestAPIOrgDeny(t *testing.T) { - defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)() + t.Run("OrgDeny", func(t *testing.T) { + defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)() - orgName := "user1_org" - req := NewRequestf(t, "GET", "/api/v1/orgs/%s", orgName) - MakeRequest(t, req, http.StatusNotFound) + orgName := "user1_org" + req := NewRequestf(t, "GET", "/api/v1/orgs/%s", orgName) + MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", orgName) - MakeRequest(t, req, http.StatusNotFound) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", orgName) + MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", orgName) - MakeRequest(t, req, http.StatusNotFound) -} + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", orgName) + MakeRequest(t, req, http.StatusNotFound) + }) -func TestAPIGetAll(t *testing.T) { - defer tests.PrepareTestEnv(t)() - token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrganization) + t.Run("OrgSearchEmptyTeam", func(t *testing.T) { + orgName := "org_with_empty_team" + // create org + req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{ + UserName: orgName, + }).AddTokenAuth(user1Token) + MakeRequest(t, req, http.StatusCreated) + + // create team with no member + req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams", orgName), &api.CreateTeamOption{ + Name: "Empty", + IncludesAllRepositories: true, + Permission: "read", + Units: []string{"repo.code", "repo.issues", "repo.ext_issues", "repo.wiki", "repo.pulls"}, + }).AddTokenAuth(user1Token) + MakeRequest(t, req, http.StatusCreated) + + // case-insensitive search for teams that have no members + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/%s/teams/search?q=%s", orgName, "empty")). + AddTokenAuth(user1Token) + resp := MakeRequest(t, req, http.StatusOK) + data := struct { + Ok bool + Data []*api.Team + }{} + DecodeJSON(t, resp, &data) + assert.True(t, data.Ok) + if assert.Len(t, data.Data, 1) { + assert.Equal(t, "Empty", data.Data[0].Name) + } + }) - // accessing with a token will return all orgs - req := NewRequest(t, "GET", "/api/v1/orgs"). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var apiOrgList []*api.Organization + t.Run("User2ChangeStatus", func(t *testing.T) { + user2Session := loginUser(t, "user2") + user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteOrganization) - DecodeJSON(t, resp, &apiOrgList) - assert.Len(t, apiOrgList, 13) - assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName) - assert.Equal(t, "limited", apiOrgList[1].Visibility) + req := NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user2").AddTokenAuth(user2Token) + MakeRequest(t, req, http.StatusNoContent) + req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user2").AddTokenAuth(user2Token) + MakeRequest(t, req, http.StatusNoContent) - // accessing without a token will return only public orgs - req = NewRequest(t, "GET", "/api/v1/orgs") - resp = MakeRequest(t, req, http.StatusOK) + // non admin but org owner could also change other member's status + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + require.False(t, user2.IsAdmin) + req = NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user2Token) + MakeRequest(t, req, http.StatusNoContent) + req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user2Token) + MakeRequest(t, req, http.StatusNoContent) + }) - DecodeJSON(t, resp, &apiOrgList) - assert.Len(t, apiOrgList, 9) - assert.Equal(t, "org 17", apiOrgList[0].FullName) - assert.Equal(t, "public", apiOrgList[0].Visibility) -} + t.Run("User4ChangeStatus", func(t *testing.T) { + user4Session := loginUser(t, "user4") + user4Token := getTokenForLoggedInUser(t, user4Session, auth_model.AccessTokenScopeWriteOrganization) -func TestAPIOrgSearchEmptyTeam(t *testing.T) { - defer tests.PrepareTestEnv(t)() - token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrganization) - orgName := "org_with_empty_team" - - // create org - req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{ - UserName: orgName, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusCreated) - - // create team with no member - req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams", orgName), &api.CreateTeamOption{ - Name: "Empty", - IncludesAllRepositories: true, - Permission: "read", - Units: []string{"repo.code", "repo.issues", "repo.ext_issues", "repo.wiki", "repo.pulls"}, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusCreated) - - // case-insensitive search for teams that have no members - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/%s/teams/search?q=%s", orgName, "empty")). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - data := struct { - Ok bool - Data []*api.Team - }{} - DecodeJSON(t, resp, &data) - assert.True(t, data.Ok) - if assert.Len(t, data.Data, 1) { - assert.Equal(t, "Empty", data.Data[0].Name) - } + // user4 is a normal team member, they could change their own status + req := NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user4").AddTokenAuth(user4Token) + MakeRequest(t, req, http.StatusNoContent) + req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user4").AddTokenAuth(user4Token) + MakeRequest(t, req, http.StatusNoContent) + req = NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user4Token) + MakeRequest(t, req, http.StatusForbidden) + req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user4Token) + MakeRequest(t, req, http.StatusForbidden) + }) } diff --git a/tests/integration/api_team_user_test.go b/tests/integration/api_team_user_test.go index adc9831aa03d4..cbbbe00a9eb52 100644 --- a/tests/integration/api_team_user_test.go +++ b/tests/integration/api_team_user_test.go @@ -21,29 +21,31 @@ import ( func TestAPITeamUser(t *testing.T) { defer tests.PrepareTestEnv(t)() - - normalUsername := "user2" - session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) - req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1"). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusNotFound) - - req = NewRequest(t, "GET", "/api/v1/teams/1/members/user2"). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var user2 *api.User - DecodeJSON(t, resp, &user2) - user2.Created = user2.Created.In(time.Local) - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) - - expectedUser := convert.ToUser(db.DefaultContext, user, user) - - // test time via unix timestamp - assert.Equal(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix()) - assert.Equal(t, expectedUser.Created.Unix(), user2.Created.Unix()) - expectedUser.LastLogin = user2.LastLogin - expectedUser.Created = user2.Created - - assert.Equal(t, expectedUser, user2) + user2Session := loginUser(t, "user2") + user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteOrganization) + + t.Run("User2ReadUser1", func(t *testing.T) { + req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1").AddTokenAuth(user2Token) + MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("User2ReadSelf", func(t *testing.T) { + // read self user + req := NewRequest(t, "GET", "/api/v1/teams/1/members/user2").AddTokenAuth(user2Token) + resp := MakeRequest(t, req, http.StatusOK) + var user2 *api.User + DecodeJSON(t, resp, &user2) + user2.Created = user2.Created.In(time.Local) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + + expectedUser := convert.ToUser(db.DefaultContext, user, user) + + // test time via unix timestamp + assert.Equal(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix()) + assert.Equal(t, expectedUser.Created.Unix(), user2.Created.Unix()) + expectedUser.LastLogin = user2.LastLogin + expectedUser.Created = user2.Created + + assert.Equal(t, expectedUser, user2) + }) }