mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-08 14:35:52 +01:00
(refs #1286) Prototyping of new permission system
This commit is contained in:
2
src/main/resources/update/gitbucket-core_4.7.sql
Normal file
2
src/main/resources/update/gitbucket-core_4.7.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- DELETE COLLABORATORS IN GROUP REPOSITORIES
|
||||||
|
DELETE FROM COLLABORATOR WHERE USER_NAME IN (SELECT USER_NAME FROM ACCOUNT WHERE GROUP_ACCOUNT = TRUE)
|
||||||
9
src/main/resources/update/gitbucket-core_4.7.xml
Normal file
9
src/main/resources/update/gitbucket-core_4.7.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<changeSet>
|
||||||
|
<addColumn tableName="COLLABORATOR">
|
||||||
|
<column name="PERMISSION" type="varchar(10)" nullable="false" defaultValue="ADMIN"/>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="REPOSITORY">
|
||||||
|
<column name="ALLOW_CREATE_ISSUE" type="boolean" nullable="false" defaultValueBoolean="false"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
@@ -18,5 +18,9 @@ object GitBucketCoreModule extends Module("gitbucket-core",
|
|||||||
new Version("4.5.0"),
|
new Version("4.5.0"),
|
||||||
new Version("4.6.0",
|
new Version("4.6.0",
|
||||||
new LiquibaseMigration("update/gitbucket-core_4.6.xml")
|
new LiquibaseMigration("update/gitbucket-core_4.6.xml")
|
||||||
|
),
|
||||||
|
new Version("4.7.0",
|
||||||
|
new LiquibaseMigration("update/gitbucket-core_4.7.xml"),
|
||||||
|
new SqlMigration("update/gitbucket-core_4.7.sql")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -109,7 +109,11 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
get("/_user/proposals")(usersOnly {
|
get("/_user/proposals")(usersOnly {
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
org.json4s.jackson.Serialization.write(
|
org.json4s.jackson.Serialization.write(
|
||||||
Map("options" -> getAllUsers(false).filter(!_.isGroupAccount).map(_.userName).toArray)
|
Map("options" -> (if(params.get("userOnly").isDefined) {
|
||||||
|
getAllUsers(false).filter(!_.isGroupAccount).map(_.userName).toArray
|
||||||
|
} else {
|
||||||
|
getAllUsers(false).map(_.userName).toArray
|
||||||
|
}))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
* Add the collaborator.
|
* Add the collaborator.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
|
||||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
getAccountByUserName(repository.owner).foreach { _ =>
|
||||||
addCollaborator(repository.owner, repository.name, form.userName)
|
addCollaborator(repository.owner, repository.name, form.userName)
|
||||||
}
|
}
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||||
@@ -192,9 +192,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
* Add the collaborator.
|
* Add the collaborator.
|
||||||
*/
|
*/
|
||||||
get("/:owner/:repository/settings/collaborators/remove")(ownerOnly { repository =>
|
get("/:owner/:repository/settings/collaborators/remove")(ownerOnly { repository =>
|
||||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
|
||||||
removeCollaborator(repository.owner, repository.name, params("name"))
|
removeCollaborator(repository.owner, repository.name, params("name"))
|
||||||
}
|
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -404,10 +402,10 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||||
getAccountByUserName(value) match {
|
getAccountByUserName(value) match {
|
||||||
case None => Some("User does not exist.")
|
case None => Some("User does not exist.")
|
||||||
case Some(x) if(x.isGroupAccount)
|
// case Some(x) if(x.isGroupAccount)
|
||||||
=> Some("User does not exist.")
|
// => Some("User does not exist.")
|
||||||
case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
|
case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
|
||||||
=> Some("User can access this repository already.")
|
=> Some(value + " is repository owner.") // TODO also group members?
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,49 +337,53 @@ trait RepositoryService { self: AccountService =>
|
|||||||
.update (defaultBranch)
|
.update (defaultBranch)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add collaborator to the repository.
|
* Add collaborator (user or group) to the repository.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
* @param collaboratorName the collaborator name
|
|
||||||
*/
|
*/
|
||||||
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit s: Session): Unit =
|
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit s: Session): Unit =
|
||||||
Collaborators insert Collaborator(userName, repositoryName, collaboratorName)
|
Collaborators insert Collaborator(userName, repositoryName, collaboratorName)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove collaborator from the repository.
|
* Remove collaborator (user or group) from the repository.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
* @param collaboratorName the collaborator name
|
|
||||||
*/
|
*/
|
||||||
def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit s: Session): Unit =
|
def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit s: Session): Unit =
|
||||||
Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete
|
Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all collaborators from the repository.
|
* Remove all collaborators from the repository.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
*/
|
*/
|
||||||
def removeCollaborators(userName: String, repositoryName: String)(implicit s: Session): Unit =
|
def removeCollaborators(userName: String, repositoryName: String)(implicit s: Session): Unit =
|
||||||
Collaborators.filter(_.byRepository(userName, repositoryName)).delete
|
Collaborators.filter(_.byRepository(userName, repositoryName)).delete
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of collaborators name which is sorted with ascending order.
|
* Returns the list of collaborators name (user name or group name) which is sorted with ascending order.
|
||||||
*
|
|
||||||
* @param userName the user name of the repository owner
|
|
||||||
* @param repositoryName the repository name
|
|
||||||
* @return the list of collaborators name
|
|
||||||
*/
|
*/
|
||||||
def getCollaborators(userName: String, repositoryName: String)(implicit s: Session): List[String] =
|
def getCollaborators(userName: String, repositoryName: String)(implicit s: Session): List[String] =
|
||||||
Collaborators.filter(_.byRepository(userName, repositoryName)).sortBy(_.collaboratorName).map(_.collaboratorName).list
|
Collaborators.filter(_.byRepository(userName, repositoryName)).sortBy(_.collaboratorName).map(_.collaboratorName).list
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of all collaborator name and permission which is sorted with ascending order.
|
||||||
|
* If a group is added as a collaborator, this method returns users who are belong to that group.
|
||||||
|
*/
|
||||||
|
def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[String] = Nil)(implicit s: Session): List[(String, String)] = {
|
||||||
|
val q1 = Collaborators.filter(_.byRepository(userName, repositoryName))
|
||||||
|
.innerJoin(Accounts).on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === false.bind) }
|
||||||
|
.map { case (t1, t2) => (t1.collaboratorName, "ADMIN") }
|
||||||
|
|
||||||
|
val q2 = Collaborators.filter(_.byRepository(userName, repositoryName))
|
||||||
|
.innerJoin(Accounts).on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === true.bind) }
|
||||||
|
.innerJoin(GroupMembers).on { case ((t1, t2), t3) => t2.userName === t3.groupName }
|
||||||
|
.map { case ((t1, t2), t3) => (t3.userName, "ADMIN") }
|
||||||
|
|
||||||
|
q1.union(q2).list
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def hasWritePermission(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
def hasWritePermission(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||||
loginAccount match {
|
loginAccount match {
|
||||||
case Some(a) if(a.isAdmin) => true
|
case Some(a) if(a.isAdmin) => true
|
||||||
case Some(a) if(a.userName == owner) => true
|
case Some(a) if(a.userName == owner) => true
|
||||||
case Some(a) if(getCollaborators(owner, repository).contains(a.userName)) => true
|
case Some(a) if(getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||||
|
case Some(a) if(getCollaboratorUserNames(owner, repository).contains((a.userName, "ADMIN"))) => true // TODO ADMIN|WRITE
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package gitbucket.core.util
|
package gitbucket.core.util
|
||||||
|
|
||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.service.{RepositoryService, AccountService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
import RepositoryService.RepositoryInfo
|
import RepositoryService.RepositoryInfo
|
||||||
import Implicits._
|
import Implicits._
|
||||||
import ControlUtil._
|
import ControlUtil._
|
||||||
|
|
||||||
|
import scala.collection.Searching.search
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only oneself and administrators.
|
* Allows only oneself and administrators.
|
||||||
*/
|
*/
|
||||||
@@ -40,9 +42,9 @@ trait OwnerAuthenticator { self: ControllerBase with RepositoryService with Acco
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(repository.owner == x.userName) => action(repository)
|
case Some(x) if(repository.owner == x.userName) => action(repository)
|
||||||
case Some(x) if(getGroupMembers(repository.owner).exists { member =>
|
// TODO Repository management is allowed for only group managers?
|
||||||
member.userName == x.userName && member.isManager == true
|
case Some(x) if(getGroupMembers(repository.owner).exists { m => m.userName == x.userName && m.isManager == true }) => action(repository)
|
||||||
}) => action(repository)
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1), Seq("ADMIN")).exists(_._1 == x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -88,7 +90,7 @@ trait AdminAuthenticator { self: ControllerBase =>
|
|||||||
/**
|
/**
|
||||||
* Allows only collaborators and administrators.
|
* Allows only collaborators and administrators.
|
||||||
*/
|
*/
|
||||||
trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =>
|
trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
protected def collaboratorsOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def collaboratorsOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
protected def collaboratorsOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def collaboratorsOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
@@ -99,7 +101,8 @@ trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
||||||
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1), Seq("ADMIN", "WRITE")).exists(_._1 == x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -109,9 +112,9 @@ trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only the repository owner (or manager for group repository) and administrators.
|
* Allows only guests and signed in users who can access the repository.
|
||||||
*/
|
*/
|
||||||
trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
trait ReferrerAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
protected def referrersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def referrersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
@@ -125,7 +128,8 @@ trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
||||||
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1)).exists(_._1 == x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,9 +140,9 @@ trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only signed in users which can access the repository.
|
* Allows only signed in users who can access the repository.
|
||||||
*/
|
*/
|
||||||
trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =>
|
trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
protected def readableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def readableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
protected def readableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def readableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
@@ -150,7 +154,8 @@ trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =
|
|||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(!repository.repository.isPrivate) => action(repository)
|
case Some(x) if(!repository.repository.isPrivate) => action(repository)
|
||||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
||||||
|
case Some(x) if(getCollaboratorUserNames(paths(0), paths(1)).exists(_._1 == x.userName)) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@(id: String, width: Int)(implicit context: gitbucket.core.controller.Context)
|
@(id: String, width: Int, userOnly: Boolean = true)(implicit context: gitbucket.core.controller.Context)
|
||||||
<span style="margin-right: 0px;">
|
<span style="margin-right: 0px;">
|
||||||
<input type="text" name="@id" id="@id" class="form-control" autocomplete="off" style="width: @{width}px; margin-bottom: 0px; display: inline; vertical-align: middle;"/>
|
<input type="text" name="@id" id="@id" class="form-control" autocomplete="off" style="width: @{width}px; margin-bottom: 0px; display: inline; vertical-align: middle;"/>
|
||||||
</span>
|
</span>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
$(function(){
|
$(function(){
|
||||||
$('#@id').typeahead({
|
$('#@id').typeahead({
|
||||||
source: function (query, process) {
|
source: function (query, process) {
|
||||||
return $.get('@context.path/_user/proposals', { query: query },
|
return $.get('@context.path/_user/proposals@if(userOnly){?userOnly}', { query: query },
|
||||||
function (data) {
|
function (data) {
|
||||||
return process(data.options);
|
return process(data.options);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,25 +10,17 @@
|
|||||||
@collaborators.map { collaboratorName =>
|
@collaborators.map { collaboratorName =>
|
||||||
<li>
|
<li>
|
||||||
<a href="@helpers.url(collaboratorName)">@collaboratorName</a>
|
<a href="@helpers.url(collaboratorName)">@collaboratorName</a>
|
||||||
@if(!isGroupRepository){
|
|
||||||
<a href="@helpers.url(repository)/settings/collaborators/remove?name=@collaboratorName" class="remove">(remove)</a>
|
<a href="@helpers.url(repository)/settings/collaborators/remove?name=@collaboratorName" class="remove">(remove)</a>
|
||||||
} else {
|
|
||||||
@if(repository.managers.contains(collaboratorName)){
|
|
||||||
(Manager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@if(!isGroupRepository){
|
|
||||||
<form method="POST" action="@helpers.url(repository)/settings/collaborators/add" validate="true" autocomplete="off">
|
<form method="POST" action="@helpers.url(repository)/settings/collaborators/add" validate="true" autocomplete="off">
|
||||||
<div>
|
<div>
|
||||||
<span class="error" id="error-userName"></span>
|
<span class="error" id="error-userName"></span>
|
||||||
</div>
|
</div>
|
||||||
@gitbucket.core.helper.html.account("userName", 300)
|
@gitbucket.core.helper.html.account("userName", 300, false)
|
||||||
<input type="submit" class="btn btn-default" value="Add"/>
|
<input type="submit" class="btn btn-default" value="Add"/>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user