From 68a83cc5a240504e7d35ed2cdc1fae889fc08a1b Mon Sep 17 00:00:00 2001
From: mrsdizzie <info@mrsdizzie.com>
Date: Thu, 16 May 2019 11:48:40 -0400
Subject: [PATCH] Allow collaborators to view repo owned private org (#6965)

Handle case where an orginization is private but a user who is not a
member of the orgninization has been added as a collaborator of a repo
within that org

Fixes #6962
---
 integrations/org_test.go          |  9 +++++++++
 models/fixtures/collaboration.yml |  6 ++++++
 models/repo_permission.go         | 16 ++++++++++++----
 3 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/integrations/org_test.go b/integrations/org_test.go
index 17b8958480..d86c82989d 100644
--- a/integrations/org_test.go
+++ b/integrations/org_test.go
@@ -92,6 +92,15 @@ func TestPrivateOrg(t *testing.T) {
 	req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
 	session.MakeRequest(t, req, http.StatusNotFound)
 
+	// non-org member who is collaborator on repo in private org
+	session = loginUser(t, "user4")
+	req = NewRequest(t, "GET", "/privated_org")
+	session.MakeRequest(t, req, http.StatusNotFound)
+	req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org") // colab of this repo
+	session.MakeRequest(t, req, http.StatusOK)
+	req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
+	session.MakeRequest(t, req, http.StatusNotFound)
+
 	// site admin
 	session = loginUser(t, "user1")
 	req = NewRequest(t, "GET", "/privated_org")
diff --git a/models/fixtures/collaboration.yml b/models/fixtures/collaboration.yml
index 18db9c36c5..d32e288e4c 100644
--- a/models/fixtures/collaboration.yml
+++ b/models/fixtures/collaboration.yml
@@ -9,3 +9,9 @@
   repo_id: 4
   user_id: 4
   mode: 2 # write
+
+-
+  id: 3
+  repo_id: 40
+  user_id: 4
+  mode: 2 # write
\ No newline at end of file
diff --git a/models/repo_permission.go b/models/repo_permission.go
index 583bc8c812..25239f4dd4 100644
--- a/models/repo_permission.go
+++ b/models/repo_permission.go
@@ -168,7 +168,17 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
 		repo.mustOwner(e)
 	}
 
-	if repo.Owner.IsOrganization() && !HasOrgVisible(repo.Owner, user) {
+	var isCollaborator bool
+	if user != nil {
+		isCollaborator, err = repo.isCollaborator(e, user.ID)
+		if err != nil {
+			return perm, err
+		}
+	}
+
+	// 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(repo.Owner, user) && !isCollaborator {
 		perm.AccessMode = AccessModeNone
 		return
 	}
@@ -207,9 +217,7 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
 	perm.UnitsMode = make(map[UnitType]AccessMode)
 
 	// Collaborators on organization
-	if isCollaborator, err := repo.isCollaborator(e, user.ID); err != nil {
-		return perm, err
-	} else if isCollaborator {
+	if isCollaborator {
 		for _, u := range repo.Units {
 			perm.UnitsMode[u.Type] = perm.AccessMode
 		}