mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-05 04:56:02 +01:00
Reject direct push to branch if branch protection is enabled (#3791)
This commit is contained in:
32
src/main/resources/update/gitbucket-core_4.44.xml
Normal file
32
src/main/resources/update/gitbucket-core_4.44.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<changeSet>
|
||||||
|
<!--================================================================================================-->
|
||||||
|
<!-- PROTECTED_BRANCH -->
|
||||||
|
<!--================================================================================================-->
|
||||||
|
<addColumn tableName="PROTECTED_BRANCH">
|
||||||
|
<column name="REQUIRED_STATUS_CHECK" type="boolean" nullable="false" defaultValue="false"/>
|
||||||
|
<column name="RESTRICTIONS" type="boolean" nullable="false" defaultValue="false"/>
|
||||||
|
</addColumn>
|
||||||
|
|
||||||
|
<sql>
|
||||||
|
UPDATE PROTECTED_BRANCH SET REQUIRED_STATUS_CHECK = TRUE
|
||||||
|
WHERE EXISTS (SELECT * FROM PROTECTED_BRANCH_REQUIRE_CONTEXT
|
||||||
|
WHERE PROTECTED_BRANCH.USER_NAME = PROTECTED_BRANCH_REQUIRE_CONTEXT.USER_NAME
|
||||||
|
AND PROTECTED_BRANCH.REPOSITORY_NAME = PROTECTED_BRANCH_REQUIRE_CONTEXT.REPOSITORY_NAME
|
||||||
|
AND PROTECTED_BRANCH.BRANCH = PROTECTED_BRANCH_REQUIRE_CONTEXT.BRANCH)
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!--================================================================================================-->
|
||||||
|
<!-- PROTECTED_BRANCH_RESTRICTIONS_USER -->
|
||||||
|
<!--================================================================================================-->
|
||||||
|
<createTable tableName="PROTECTED_BRANCH_RESTRICTION">
|
||||||
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
|
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||||
|
<column name="BRANCH" type="varchar(100)" nullable="false"/>
|
||||||
|
<column name="ALLOWED_USER" type="varchar(255)" nullable="false"/>
|
||||||
|
</createTable>
|
||||||
|
|
||||||
|
<addPrimaryKey constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_PK" tableName="PROTECTED_BRANCH_RESTRICTION" columnNames="USER_NAME, REPOSITORY_NAME, BRANCH, ALLOWED_USER"/>
|
||||||
|
<addForeignKeyConstraint constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_FK0" baseTableName="PROTECTED_BRANCH_RESTRICTION" baseColumnNames="USER_NAME, REPOSITORY_NAME, BRANCH" referencedTableName="PROTECTED_BRANCH" referencedColumnNames="USER_NAME, REPOSITORY_NAME, BRANCH" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||||
|
<addForeignKeyConstraint constraintName="IDX_PROTECTED_BRANCH_RESTRICTION_FK1" baseTableName="PROTECTED_BRANCH_RESTRICTION" baseColumnNames="ALLOWED_USER" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
||||||
|
</changeSet>
|
||||||
@@ -120,7 +120,8 @@ object GitBucketCoreModule
|
|||||||
new Version("4.41.0"),
|
new Version("4.41.0"),
|
||||||
new Version("4.42.0", new LiquibaseMigration("update/gitbucket-core_4.42.xml")),
|
new Version("4.42.0", new LiquibaseMigration("update/gitbucket-core_4.42.xml")),
|
||||||
new Version("4.42.1"),
|
new Version("4.42.1"),
|
||||||
new Version("4.43.0")
|
new Version("4.43.0"),
|
||||||
|
new Version("4.44.0", new LiquibaseMigration("update/gitbucket-core_4.44.xml"))
|
||||||
) {
|
) {
|
||||||
java.util.logging.Logger.getLogger("liquibase").setLevel(Level.SEVERE)
|
java.util.logging.Logger.getLogger("liquibase").setLevel(Level.SEVERE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import gitbucket.core.util.RepositoryName
|
|||||||
* https://developer.github.com/v3/repos/#get-branch
|
* https://developer.github.com/v3/repos/#get-branch
|
||||||
* https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection
|
* https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection
|
||||||
*/
|
*/
|
||||||
case class ApiBranch(name: String, commit: ApiBranchCommit, protection: ApiBranchProtection)(
|
case class ApiBranch(name: String, commit: ApiBranchCommit, protection: ApiBranchProtectionResponse)(
|
||||||
repositoryName: RepositoryName
|
repositoryName: RepositoryName
|
||||||
) extends FieldSerializable {
|
) extends FieldSerializable {
|
||||||
val _links =
|
val _links =
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package gitbucket.core.api
|
||||||
|
|
||||||
|
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */
|
||||||
|
case class ApiBranchProtectionRequest(
|
||||||
|
enabled: Boolean,
|
||||||
|
required_status_checks: Option[ApiBranchProtectionRequest.Status],
|
||||||
|
restrictions: Option[ApiBranchProtectionRequest.Restrictions],
|
||||||
|
enforce_admins: Option[Boolean]
|
||||||
|
)
|
||||||
|
|
||||||
|
object ApiBranchProtectionRequest {
|
||||||
|
|
||||||
|
/** form for enabling-and-disabling-branch-protection */
|
||||||
|
case class EnablingAndDisabling(protection: ApiBranchProtectionRequest)
|
||||||
|
|
||||||
|
case class Status(
|
||||||
|
contexts: Seq[String]
|
||||||
|
)
|
||||||
|
|
||||||
|
case class Restrictions(users: Seq[String])
|
||||||
|
}
|
||||||
@@ -4,55 +4,68 @@ import gitbucket.core.service.ProtectedBranchService
|
|||||||
import org.json4s._
|
import org.json4s._
|
||||||
|
|
||||||
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */
|
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */
|
||||||
case class ApiBranchProtection(
|
case class ApiBranchProtectionResponse(
|
||||||
url: Option[ApiPath], // for output
|
url: Option[ApiPath], // for output
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
required_status_checks: Option[ApiBranchProtection.Status]
|
required_status_checks: Option[ApiBranchProtectionResponse.Status],
|
||||||
|
restrictions: Option[ApiBranchProtectionResponse.Restrictions],
|
||||||
|
enforce_admins: Option[ApiBranchProtectionResponse.EnforceAdmins]
|
||||||
) {
|
) {
|
||||||
def status: ApiBranchProtection.Status = required_status_checks.getOrElse(ApiBranchProtection.statusNone)
|
def status: ApiBranchProtectionResponse.Status =
|
||||||
|
required_status_checks.getOrElse(ApiBranchProtectionResponse.statusNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ApiBranchProtection {
|
object ApiBranchProtectionResponse {
|
||||||
|
|
||||||
/** form for enabling-and-disabling-branch-protection */
|
case class EnforceAdmins(enabled: Boolean)
|
||||||
case class EnablingAndDisabling(protection: ApiBranchProtection)
|
|
||||||
|
|
||||||
def apply(info: ProtectedBranchService.ProtectedBranchInfo): ApiBranchProtection =
|
// /** form for enabling-and-disabling-branch-protection */
|
||||||
ApiBranchProtection(
|
// case class EnablingAndDisabling(protection: ApiBranchProtectionResponse)
|
||||||
|
|
||||||
|
def apply(info: ProtectedBranchService.ProtectedBranchInfo): ApiBranchProtectionResponse =
|
||||||
|
ApiBranchProtectionResponse(
|
||||||
url = Some(
|
url = Some(
|
||||||
ApiPath(
|
ApiPath(
|
||||||
s"/api/v3/repos/${info.owner}/${info.repository}/branches/${info.branch}/protection"
|
s"/api/v3/repos/${info.owner}/${info.repository}/branches/${info.branch}/protection"
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
enabled = info.enabled,
|
enabled = info.enabled,
|
||||||
required_status_checks = Some(
|
required_status_checks = info.contexts.map { contexts =>
|
||||||
Status(
|
Status(
|
||||||
Some(
|
Some(
|
||||||
ApiPath(
|
ApiPath(
|
||||||
s"/api/v3/repos/${info.owner}/${info.repository}/branches/${info.branch}/protection/required_status_checks"
|
s"/api/v3/repos/${info.owner}/${info.repository}/branches/${info.branch}/protection/required_status_checks"
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
EnforcementLevel(info.enabled && info.contexts.nonEmpty, info.includeAdministrators),
|
EnforcementLevel(info.enabled && info.contexts.nonEmpty, info.enforceAdmins),
|
||||||
info.contexts,
|
contexts,
|
||||||
Some(
|
Some(
|
||||||
ApiPath(
|
ApiPath(
|
||||||
s"/api/v3/repos/${info.owner}/${info.repository}/branches/${info.branch}/protection/required_status_checks/contexts"
|
s"/api/v3/repos/${info.owner}/${info.repository}/branches/${info.branch}/protection/required_status_checks/contexts"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
},
|
||||||
|
restrictions = info.restrictionsUsers.map { restrictionsUsers =>
|
||||||
|
Restrictions(restrictionsUsers)
|
||||||
|
},
|
||||||
|
enforce_admins = if (info.enabled) Some(EnforceAdmins(info.enforceAdmins)) else None
|
||||||
)
|
)
|
||||||
val statusNone = Status(None, Off, Seq.empty, None)
|
|
||||||
|
val statusNone: Status = Status(None, Off, Seq.empty, None)
|
||||||
|
|
||||||
case class Status(
|
case class Status(
|
||||||
url: Option[ApiPath], // for output
|
url: Option[ApiPath], // for output
|
||||||
enforcement_level: EnforcementLevel,
|
enforcement_level: EnforcementLevel,
|
||||||
contexts: Seq[String],
|
contexts: Seq[String],
|
||||||
contexts_url: Option[ApiPath] // for output
|
contexts_url: Option[ApiPath] // for output
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class EnforcementLevel(val name: String)
|
sealed class EnforcementLevel(val name: String)
|
||||||
case object Off extends EnforcementLevel("off")
|
case object Off extends EnforcementLevel("off")
|
||||||
case object NonAdmins extends EnforcementLevel("non_admins")
|
case object NonAdmins extends EnforcementLevel("non_admins")
|
||||||
case object Everyone extends EnforcementLevel("everyone")
|
case object Everyone extends EnforcementLevel("everyone")
|
||||||
|
|
||||||
object EnforcementLevel {
|
object EnforcementLevel {
|
||||||
def apply(enabled: Boolean, includeAdministrators: Boolean): EnforcementLevel =
|
def apply(enabled: Boolean, includeAdministrators: Boolean): EnforcementLevel =
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
@@ -66,6 +79,8 @@ object ApiBranchProtection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class Restrictions(users: Seq[String])
|
||||||
|
|
||||||
implicit val enforcementLevelSerializer: CustomSerializer[EnforcementLevel] =
|
implicit val enforcementLevelSerializer: CustomSerializer[EnforcementLevel] =
|
||||||
new CustomSerializer[EnforcementLevel](format =>
|
new CustomSerializer[EnforcementLevel](format =>
|
||||||
(
|
(
|
||||||
@@ -44,7 +44,7 @@ object JsonFormat {
|
|||||||
FieldSerializer[ApiCommits.File]() +
|
FieldSerializer[ApiCommits.File]() +
|
||||||
FieldSerializer[ApiRelease]() +
|
FieldSerializer[ApiRelease]() +
|
||||||
FieldSerializer[ApiReleaseAsset]() +
|
FieldSerializer[ApiReleaseAsset]() +
|
||||||
ApiBranchProtection.enforcementLevelSerializer
|
ApiBranchProtectionResponse.enforcementLevelSerializer
|
||||||
|
|
||||||
def apiPathSerializer(c: Context) =
|
def apiPathSerializer(c: Context) =
|
||||||
new CustomSerializer[ApiPath](_ =>
|
new CustomSerializer[ApiPath](_ =>
|
||||||
|
|||||||
@@ -261,11 +261,22 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON API for checking user or group existence.
|
* JSON API for checking user or group existence.
|
||||||
|
*
|
||||||
* Returns a single string which is any of "group", "user" or "".
|
* Returns a single string which is any of "group", "user" or "".
|
||||||
|
* Additionally, check whether the user is writable to the repository
|
||||||
|
* if "owner" and "repository" are given,
|
||||||
*/
|
*/
|
||||||
post("/_user/existence")(usersOnly {
|
post("/_user/existence")(usersOnly {
|
||||||
getAccountByUserNameIgnoreCase(params("userName")).map { account =>
|
getAccountByUserNameIgnoreCase(params("userName")).map { account =>
|
||||||
if (account.isGroupAccount) "group" else "user"
|
if (!account.isGroupAccount && params.get("repository").isDefined && params.get("owner").isDefined) {
|
||||||
|
getRepository(params("owner"), params("repository"))
|
||||||
|
.collect {
|
||||||
|
case repository if isWritable(repository.repository, Some(account)) => "user"
|
||||||
|
}
|
||||||
|
.getOrElse("")
|
||||||
|
} else {
|
||||||
|
if (account.isGroupAccount) "group" else "user"
|
||||||
|
}
|
||||||
} getOrElse ""
|
} getOrElse ""
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
if (!repository.branchList.contains(branch)) {
|
if (!repository.branchList.contains(branch)) {
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
||||||
} else {
|
} else {
|
||||||
val protection = ApiBranchProtection(getProtectedBranchInfo(repository.owner, repository.name, branch))
|
val protection = ApiBranchProtectionResponse(getProtectedBranchInfo(repository.owner, repository.name, branch))
|
||||||
val lastWeeks = getRecentStatusContexts(
|
val lastWeeks = getRecentStatusContexts(
|
||||||
repository.owner,
|
repository.owner,
|
||||||
repository.name,
|
repository.name,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import gitbucket.core.service.{AccountService, ProtectedBranchService, Repositor
|
|||||||
import gitbucket.core.util.*
|
import gitbucket.core.util.*
|
||||||
import gitbucket.core.util.Directory.*
|
import gitbucket.core.util.Directory.*
|
||||||
import gitbucket.core.util.Implicits.*
|
import gitbucket.core.util.Implicits.*
|
||||||
import gitbucket.core.util.JGitUtil.getBranchesNoMergeInfo
|
import gitbucket.core.util.JGitUtil.{getBranchesNoMergeInfo, processTree}
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.scalatra.NoContent
|
import org.scalatra.NoContent
|
||||||
|
|
||||||
@@ -43,7 +43,9 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
} yield {
|
} yield {
|
||||||
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository))
|
ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtectionResponse(protection))(
|
||||||
|
RepositoryName(repository)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
}
|
}
|
||||||
@@ -58,7 +60,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
if (repository.branchList.contains(branch)) {
|
if (repository.branchList.contains(branch)) {
|
||||||
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
ApiBranchProtection(protection)
|
ApiBranchProtectionResponse(protection)
|
||||||
)
|
)
|
||||||
} else { NotFound() }
|
} else { NotFound() }
|
||||||
})
|
})
|
||||||
@@ -138,7 +140,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
if (repository.branchList.contains(branch)) {
|
if (repository.branchList.contains(branch)) {
|
||||||
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
ApiBranchProtection(protection).required_status_checks
|
ApiBranchProtectionResponse(protection).required_status_checks
|
||||||
)
|
)
|
||||||
} else { NotFound() }
|
} else { NotFound() }
|
||||||
})
|
})
|
||||||
@@ -262,7 +264,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
(for {
|
(for {
|
||||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||||
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
|
protection <- extractFromJsonBody[ApiBranchProtectionRequest.EnablingAndDisabling].map(_.protection)
|
||||||
br <- getBranchesNoMergeInfo(git).find(_.name == branch)
|
br <- getBranchesNoMergeInfo(git).find(_.name == branch)
|
||||||
} yield {
|
} yield {
|
||||||
if (protection.enabled) {
|
if (protection.enabled) {
|
||||||
@@ -270,13 +272,17 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
repository.owner,
|
repository.owner,
|
||||||
repository.name,
|
repository.name,
|
||||||
branch,
|
branch,
|
||||||
protection.status.enforcement_level == ApiBranchProtection.Everyone,
|
protection.enforce_admins.getOrElse(false),
|
||||||
protection.status.contexts
|
protection.required_status_checks.isDefined,
|
||||||
|
protection.required_status_checks.map(_.contexts).getOrElse(Nil),
|
||||||
|
protection.restrictions.isDefined,
|
||||||
|
protection.restrictions.map(_.users).getOrElse(Nil)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
disableBranchProtection(repository.owner, repository.name, branch)
|
disableBranchProtection(repository.owner, repository.name, branch)
|
||||||
}
|
}
|
||||||
JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository)))
|
val response = ApiBranchProtectionResponse(getProtectedBranchInfo(repository.owner, repository.name, branch))
|
||||||
|
JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), response)(RepositoryName(repository)))
|
||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
package gitbucket.core.model
|
package gitbucket.core.model
|
||||||
|
|
||||||
trait ProtectedBranchComponent extends TemplateComponent { self: Profile =>
|
trait ProtectedBranchComponent extends TemplateComponent { self: Profile =>
|
||||||
import profile.api._
|
import profile.api.*
|
||||||
import self._
|
|
||||||
|
|
||||||
lazy val ProtectedBranches = TableQuery[ProtectedBranches]
|
lazy val ProtectedBranches = TableQuery[ProtectedBranches]
|
||||||
class ProtectedBranches(tag: Tag) extends Table[ProtectedBranch](tag, "PROTECTED_BRANCH") with BranchTemplate {
|
class ProtectedBranches(tag: Tag) extends Table[ProtectedBranch](tag, "PROTECTED_BRANCH") with BranchTemplate {
|
||||||
val statusCheckAdmin = column[Boolean]("STATUS_CHECK_ADMIN")
|
val statusCheckAdmin = column[Boolean]("STATUS_CHECK_ADMIN") // enforceAdmins
|
||||||
def * = (userName, repositoryName, branch, statusCheckAdmin).mapTo[ProtectedBranch]
|
val requiredStatusCheck = column[Boolean]("REQUIRED_STATUS_CHECK")
|
||||||
def byPrimaryKey(userName: String, repositoryName: String, branch: String) =
|
val restrictions = column[Boolean]("RESTRICTIONS")
|
||||||
|
def * =
|
||||||
|
(userName, repositoryName, branch, statusCheckAdmin, requiredStatusCheck, restrictions).mapTo[ProtectedBranch]
|
||||||
|
def byPrimaryKey(userName: String, repositoryName: String, branch: String): Rep[Boolean] =
|
||||||
byBranch(userName, repositoryName, branch)
|
byBranch(userName, repositoryName, branch)
|
||||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], branch: Rep[String]) =
|
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], branch: Rep[String]): Rep[Boolean] =
|
||||||
byBranch(userName, repositoryName, branch)
|
byBranch(userName, repositoryName, branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +24,27 @@ trait ProtectedBranchComponent extends TemplateComponent { self: Profile =>
|
|||||||
def * =
|
def * =
|
||||||
(userName, repositoryName, branch, context).mapTo[ProtectedBranchContext]
|
(userName, repositoryName, branch, context).mapTo[ProtectedBranchContext]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy val ProtectedBranchRestrictions = TableQuery[ProtectedBranchRestrictions]
|
||||||
|
class ProtectedBranchRestrictions(tag: Tag)
|
||||||
|
extends Table[ProtectedBranchRestriction](tag, "PROTECTED_BRANCH_RESTRICTION")
|
||||||
|
with BranchTemplate {
|
||||||
|
val allowedUser = column[String]("ALLOWED_USER")
|
||||||
|
def * = (userName, repositoryName, branch, allowedUser).mapTo[ProtectedBranchRestriction]
|
||||||
|
def byPrimaryKey(userName: String, repositoryName: String, branch: String, allowedUser: String): Rep[Boolean] =
|
||||||
|
this.userName === userName.bind && this.repositoryName === repositoryName.bind && this.branch === branch.bind && this.allowedUser === allowedUser.bind
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ProtectedBranch(userName: String, repositoryName: String, branch: String, statusCheckAdmin: Boolean)
|
case class ProtectedBranch(
|
||||||
|
userName: String,
|
||||||
|
repositoryName: String,
|
||||||
|
branch: String,
|
||||||
|
enforceAdmins: Boolean,
|
||||||
|
requiredStatusCheck: Boolean,
|
||||||
|
restrictions: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
case class ProtectedBranchContext(userName: String, repositoryName: String, branch: String, context: String)
|
case class ProtectedBranchContext(userName: String, repositoryName: String, branch: String, context: String)
|
||||||
|
|
||||||
|
case class ProtectedBranchRestriction(userName: String, repositoryName: String, branch: String, allowedUser: String)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package gitbucket.core.service
|
package gitbucket.core.service
|
||||||
|
|
||||||
import gitbucket.core.model.{Session => _, _}
|
|
||||||
import gitbucket.core.plugin.ReceiveHook
|
import gitbucket.core.plugin.ReceiveHook
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile.*
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi.*
|
||||||
|
import gitbucket.core.model.{CommitState, ProtectedBranch, ProtectedBranchContext, ProtectedBranchRestriction, Role}
|
||||||
|
import gitbucket.core.util.SyntaxSugars.*
|
||||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||||
|
|
||||||
trait ProtectedBranchService {
|
trait ProtectedBranchService {
|
||||||
@@ -13,17 +14,27 @@ trait ProtectedBranchService {
|
|||||||
): Option[ProtectedBranchInfo] =
|
): Option[ProtectedBranchInfo] =
|
||||||
ProtectedBranches
|
ProtectedBranches
|
||||||
.joinLeft(ProtectedBranchContexts)
|
.joinLeft(ProtectedBranchContexts)
|
||||||
.on { case (pb, c) => pb.byBranch(c.userName, c.repositoryName, c.branch) }
|
.on { case pb ~ c => pb.byBranch(c.userName, c.repositoryName, c.branch) }
|
||||||
.map { case (pb, c) => pb -> c.map(_.context) }
|
.joinLeft(ProtectedBranchRestrictions)
|
||||||
|
.on { case pb ~ c ~ r => pb.byBranch(r.userName, r.repositoryName, r.branch) }
|
||||||
|
.map { case pb ~ c ~ r => pb -> (c.map(_.context), r.map(_.allowedUser)) }
|
||||||
.filter(_._1.byPrimaryKey(owner, repository, branch))
|
.filter(_._1.byPrimaryKey(owner, repository, branch))
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
.headOption
|
.headOption
|
||||||
.map { p =>
|
.map { (p: (ProtectedBranch, List[(ProtectedBranch, (Option[String], Option[String]))])) =>
|
||||||
p._1 -> p._2.flatMap(_._2)
|
p._1 -> (p._2.flatMap(_._2._1), p._2.flatMap(_._2._2))
|
||||||
}
|
}
|
||||||
.map { case (t1, contexts) =>
|
.map { case (t1, (contexts, users)) =>
|
||||||
new ProtectedBranchInfo(t1.userName, t1.repositoryName, t1.branch, true, contexts, t1.statusCheckAdmin)
|
new ProtectedBranchInfo(
|
||||||
|
t1.userName,
|
||||||
|
t1.repositoryName,
|
||||||
|
t1.branch,
|
||||||
|
true,
|
||||||
|
if (t1.requiredStatusCheck) Some(contexts) else None,
|
||||||
|
t1.enforceAdmins,
|
||||||
|
if (t1.restrictions) Some(users) else None
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getProtectedBranchInfo(owner: String, repository: String, branch: String)(implicit
|
def getProtectedBranchInfo(owner: String, repository: String, branch: String)(implicit
|
||||||
@@ -40,19 +51,32 @@ trait ProtectedBranchService {
|
|||||||
owner: String,
|
owner: String,
|
||||||
repository: String,
|
repository: String,
|
||||||
branch: String,
|
branch: String,
|
||||||
includeAdministrators: Boolean,
|
enforceAdmins: Boolean,
|
||||||
contexts: Seq[String]
|
requiredStatusCheck: Boolean,
|
||||||
|
contexts: Seq[String],
|
||||||
|
restrictions: Boolean,
|
||||||
|
restrictionsUsers: Seq[String]
|
||||||
)(implicit session: Session): Unit = {
|
)(implicit session: Session): Unit = {
|
||||||
disableBranchProtection(owner, repository, branch)
|
disableBranchProtection(owner, repository, branch)
|
||||||
ProtectedBranches.insert(new ProtectedBranch(owner, repository, branch, includeAdministrators && contexts.nonEmpty))
|
ProtectedBranches.insert(
|
||||||
contexts.map { context =>
|
ProtectedBranch(owner, repository, branch, enforceAdmins, requiredStatusCheck, restrictions)
|
||||||
ProtectedBranchContexts.insert(new ProtectedBranchContext(owner, repository, branch, context))
|
)
|
||||||
|
|
||||||
|
if (restrictions) {
|
||||||
|
restrictionsUsers.foreach { user =>
|
||||||
|
ProtectedBranchRestrictions.insert(ProtectedBranchRestriction(owner, repository, branch, user))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiredStatusCheck) {
|
||||||
|
contexts.foreach { context =>
|
||||||
|
ProtectedBranchContexts.insert(ProtectedBranchContext(owner, repository, branch, context))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def disableBranchProtection(owner: String, repository: String, branch: String)(implicit session: Session): Unit =
|
def disableBranchProtection(owner: String, repository: String, branch: String)(implicit session: Session): Unit =
|
||||||
ProtectedBranches.filter(_.byPrimaryKey(owner, repository, branch)).delete
|
ProtectedBranches.filter(_.byPrimaryKey(owner, repository, branch)).delete
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ProtectedBranchService {
|
object ProtectedBranchService {
|
||||||
@@ -101,6 +125,7 @@ object ProtectedBranchService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
println("-> else")
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,12 +142,16 @@ object ProtectedBranchService {
|
|||||||
* When enabled, commits must first be pushed to another branch,
|
* When enabled, commits must first be pushed to another branch,
|
||||||
* then merged or pushed directly to test after status checks have passed.
|
* then merged or pushed directly to test after status checks have passed.
|
||||||
*/
|
*/
|
||||||
contexts: Seq[String],
|
contexts: Option[Seq[String]],
|
||||||
/**
|
/**
|
||||||
* Include administrators
|
* Include administrators
|
||||||
* Enforce required status checks for repository administrators.
|
* Enforce required status checks for repository administrators.
|
||||||
*/
|
*/
|
||||||
includeAdministrators: Boolean
|
enforceAdmins: Boolean,
|
||||||
|
/**
|
||||||
|
* Users who can push to the branch.
|
||||||
|
*/
|
||||||
|
restrictionsUsers: Option[Seq[String]]
|
||||||
) extends AccountService
|
) extends AccountService
|
||||||
with RepositoryService
|
with RepositoryService
|
||||||
with CommitStatusService {
|
with CommitStatusService {
|
||||||
@@ -148,42 +177,66 @@ object ProtectedBranchService {
|
|||||||
session: Session
|
session: Session
|
||||||
): Option[String] = {
|
): Option[String] = {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
command.getType() match {
|
command.getType match {
|
||||||
case ReceiveCommand.Type.UPDATE_NONFASTFORWARD if isAllowNonFastForwards =>
|
case ReceiveCommand.Type.UPDATE_NONFASTFORWARD if isAllowNonFastForwards =>
|
||||||
Some("Cannot force-push to a protected branch")
|
Some("Cannot force-push to a protected branch")
|
||||||
|
case ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD if !isPushAllowed(pusher) =>
|
||||||
|
Some("You do not have permission to push to this branch")
|
||||||
case ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) =>
|
case ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) =>
|
||||||
unSuccessedContexts(command.getNewId.name) match {
|
unSuccessedContexts(command.getNewId.name) match {
|
||||||
case s if s.sizeIs == 1 => Some(s"""Required status check "${s.toSeq(0)}" is expected""")
|
case s if s.sizeIs == 1 => Some(s"""Required status check "${s.head}" is expected""")
|
||||||
case s if s.sizeIs >= 1 => Some(s"${s.size} of ${contexts.size} required status checks are expected")
|
case s if s.sizeIs >= 1 =>
|
||||||
case _ => None
|
Some(s"${s.size} of ${contexts.map(_.size).getOrElse(0)} required status checks are expected")
|
||||||
|
case _ => None
|
||||||
}
|
}
|
||||||
case ReceiveCommand.Type.DELETE =>
|
case ReceiveCommand.Type.DELETE =>
|
||||||
Some("Cannot delete a protected branch")
|
Some("You do not have permission to push to this branch")
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def unSuccessedContexts(sha1: String)(implicit session: Session): Set[String] =
|
|
||||||
if (contexts.isEmpty) {
|
def unSuccessedContexts(sha1: String)(implicit session: Session): Set[String] = {
|
||||||
Set.empty
|
contexts match {
|
||||||
} else {
|
case None => Set.empty
|
||||||
contexts.toSet -- getCommitStatuses(owner, repository, sha1)
|
case Some(x) if x.isEmpty => Set.empty
|
||||||
.filter(_.state == CommitState.SUCCESS)
|
case Some(x) =>
|
||||||
.map(_.context)
|
x.toSet -- getCommitStatuses(owner, repository, sha1)
|
||||||
.toSet
|
.filter(_.state == CommitState.SUCCESS)
|
||||||
|
.map(_.context)
|
||||||
|
.toSet
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def needStatusCheck(pusher: String)(implicit session: Session): Boolean = pusher match {
|
def needStatusCheck(pusher: String)(implicit session: Session): Boolean = pusher match {
|
||||||
case _ if !enabled => false
|
case _ if !enabled => false
|
||||||
case _ if contexts.isEmpty => false
|
case _ if contexts.isEmpty => false
|
||||||
case _ if includeAdministrators => true
|
case _ if enforceAdmins => true
|
||||||
case p if isAdministrator(p) => false
|
case p if isAdministrator(p) => false
|
||||||
case _ => true
|
case _ => true
|
||||||
|
}
|
||||||
|
|
||||||
|
def isPushAllowed(pusher: String)(implicit session: Session): Boolean = pusher match {
|
||||||
|
case _ if !enabled || restrictionsUsers.isEmpty => true
|
||||||
|
case _ if restrictionsUsers.get.contains(pusher) => true
|
||||||
|
case p if isAdministrator(p) && enforceAdmins => false
|
||||||
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ProtectedBranchInfo {
|
object ProtectedBranchInfo {
|
||||||
def disabled(owner: String, repository: String, branch: String): ProtectedBranchInfo =
|
def disabled(owner: String, repository: String, branch: String): ProtectedBranchInfo = {
|
||||||
ProtectedBranchInfo(owner, repository, branch, false, Nil, false)
|
ProtectedBranchInfo(
|
||||||
|
owner,
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
enabled = false,
|
||||||
|
contexts = None,
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -653,18 +653,18 @@ object PullRequestService {
|
|||||||
commitIdTo: String
|
commitIdTo: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val hasConflict = conflictMessage.isDefined
|
val hasConflict: Boolean = conflictMessage.isDefined
|
||||||
val statuses: List[CommitStatus] =
|
val statuses: List[CommitStatus] =
|
||||||
commitStatuses ++ (branchProtection.contexts.toSet -- commitStatuses.map(_.context).toSet)
|
commitStatuses ++ (branchProtection.contexts.getOrElse(Nil).toSet -- commitStatuses.map(_.context).toSet)
|
||||||
.map(CommitStatus.pending(branchProtection.owner, branchProtection.repository, _))
|
.map(CommitStatus.pending(branchProtection.owner, branchProtection.repository, _))
|
||||||
val hasRequiredStatusProblem = needStatusCheck && branchProtection.contexts.exists(context =>
|
val hasRequiredStatusProblem: Boolean = needStatusCheck && branchProtection.contexts
|
||||||
statuses.find(_.context == context).map(_.state) != Some(CommitState.SUCCESS)
|
.getOrElse(Nil)
|
||||||
)
|
.exists(context => !statuses.find(_.context == context).map(_.state).contains(CommitState.SUCCESS))
|
||||||
val hasProblem = hasRequiredStatusProblem || hasConflict || (statuses.nonEmpty && CommitState.combine(
|
val hasProblem: Boolean = hasRequiredStatusProblem || hasConflict || (statuses.nonEmpty && CommitState.combine(
|
||||||
statuses.map(_.state).toSet
|
statuses.map(_.state).toSet
|
||||||
) != CommitState.SUCCESS)
|
) != CommitState.SUCCESS)
|
||||||
val canUpdate = branchIsOutOfDate && !hasConflict
|
val canUpdate: Boolean = branchIsOutOfDate && !hasConflict
|
||||||
val canMerge = hasMergePermission && !hasConflict && !hasRequiredStatusProblem
|
val canMerge: Boolean = hasMergePermission && !hasConflict && !hasRequiredStatusProblem
|
||||||
lazy val commitStateSummary: (CommitState, String) = {
|
lazy val commitStateSummary: (CommitState, String) = {
|
||||||
val stateMap = statuses.groupBy(_.state)
|
val stateMap = statuses.groupBy(_.state)
|
||||||
val state = CommitState.combine(stateMap.keySet)
|
val state = CommitState.combine(stateMap.keySet)
|
||||||
@@ -672,8 +672,8 @@ object PullRequestService {
|
|||||||
state -> summary
|
state -> summary
|
||||||
}
|
}
|
||||||
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
||||||
s -> branchProtection.contexts.contains(s.context)
|
s -> branchProtection.contexts.getOrElse(Nil).contains(s.context)
|
||||||
}
|
}
|
||||||
lazy val isAllSuccess = commitStateSummary._1 == CommitState.SUCCESS
|
lazy val isAllSuccess: Boolean = commitStateSummary._1 == CommitState.SUCCESS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package gitbucket.core.service
|
package gitbucket.core.service
|
||||||
import gitbucket.core.api.JsonFormat
|
import gitbucket.core.api.JsonFormat
|
||||||
import gitbucket.core.model.{Account, WebHook}
|
import gitbucket.core.model.{Account, WebHook}
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi.*
|
||||||
import gitbucket.core.model.activity.{CloseIssueInfo, PushInfo}
|
import gitbucket.core.model.activity.{CloseIssueInfo, PushInfo}
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
@@ -11,14 +11,14 @@ import gitbucket.core.util.JGitUtil.CommitInfo
|
|||||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib.*
|
||||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||||
|
|
||||||
import scala.util.Using
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositoryCommitFileService {
|
trait RepositoryCommitFileService {
|
||||||
self: AccountService & ActivityService & IssuesService & PullRequestService & WebHookPullRequestService &
|
self: AccountService & ActivityService & IssuesService & PullRequestService & WebHookPullRequestService &
|
||||||
RepositoryService =>
|
RepositoryService & ProtectedBranchService =>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create multiple files by callback function.
|
* Create multiple files by callback function.
|
||||||
@@ -92,10 +92,10 @@ trait RepositoryCommitFileService {
|
|||||||
)(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, Option[ObjectId])] = {
|
)(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, Option[ObjectId])] = {
|
||||||
|
|
||||||
val newPath = newFileName.map { newFileName =>
|
val newPath = newFileName.map { newFileName =>
|
||||||
if (path.length == 0) newFileName else s"${path}/${newFileName}"
|
if (path.isEmpty) newFileName else s"${path}/${newFileName}"
|
||||||
}
|
}
|
||||||
val oldPath = oldFileName.map { oldFileName =>
|
val oldPath = oldFileName.map { oldFileName =>
|
||||||
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
if (path.isEmpty) oldFileName else s"${path}/${oldFileName}"
|
||||||
}
|
}
|
||||||
|
|
||||||
_createFiles(repository, branch, message, pusherAccount, committerName, committerMailAddress, settings) {
|
_createFiles(repository, branch, message, pusherAccount, committerName, committerMailAddress, settings) {
|
||||||
@@ -139,7 +139,6 @@ trait RepositoryCommitFileService {
|
|||||||
)(
|
)(
|
||||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => R
|
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => R
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, R)] = {
|
)(implicit s: Session, c: JsonFormat.Context): Either[String, (ObjectId, R)] = {
|
||||||
|
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
@@ -168,7 +167,14 @@ trait RepositoryCommitFileService {
|
|||||||
|
|
||||||
// call pre-commit hook
|
// call pre-commit hook
|
||||||
val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
|
val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
|
||||||
hook.preReceive(repository.owner, repository.name, receivePack, receiveCommand, pusherAccount.userName, false)
|
hook.preReceive(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
receivePack,
|
||||||
|
receiveCommand,
|
||||||
|
pusherAccount.userName,
|
||||||
|
mergePullRequest = false
|
||||||
|
)
|
||||||
}.headOption
|
}.headOption
|
||||||
|
|
||||||
error match {
|
error match {
|
||||||
@@ -194,7 +200,8 @@ trait RepositoryCommitFileService {
|
|||||||
// record activity
|
// record activity
|
||||||
updateLastActivityDate(repository.owner, repository.name)
|
updateLastActivityDate(repository.owner, repository.name)
|
||||||
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||||
val pushInfo = PushInfo(repository.owner, repository.name, pusherAccount.userName, branch, List(commitInfo))
|
val pushInfo =
|
||||||
|
PushInfo(repository.owner, repository.name, pusherAccount.userName, branch, List(commitInfo))
|
||||||
recordActivity(pushInfo)
|
recordActivity(pushInfo)
|
||||||
|
|
||||||
// create issue comment by commit message
|
// create issue comment by commit message
|
||||||
@@ -221,7 +228,14 @@ trait RepositoryCommitFileService {
|
|||||||
|
|
||||||
// call post-commit hook
|
// call post-commit hook
|
||||||
PluginRegistry().getReceiveHooks.foreach { hook =>
|
PluginRegistry().getReceiveHooks.foreach { hook =>
|
||||||
hook.postReceive(repository.owner, repository.name, receivePack, receiveCommand, committerName, false)
|
hook.postReceive(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
receivePack,
|
||||||
|
receiveCommand,
|
||||||
|
committerName,
|
||||||
|
mergePullRequest = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||||
|
|||||||
@@ -654,11 +654,11 @@ trait RepositoryService {
|
|||||||
|
|
||||||
def hasOwnerRole(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
def hasOwnerRole(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 (getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
case Some(a) if getGroupMembers(owner).exists(_.userName == a.userName) => true
|
||||||
case Some(a) if (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN)).contains(a.userName)) => true
|
case Some(a) if getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN)).contains(a.userName) => true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,11 +666,11 @@ trait RepositoryService {
|
|||||||
s: Session
|
s: Session
|
||||||
): Boolean = {
|
): 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 (getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
case Some(a) if getGroupMembers(owner).exists(_.userName == a.userName) => true
|
||||||
case Some(a)
|
case Some(a)
|
||||||
if (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)).contains(a.userName)) =>
|
if getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)).contains(a.userName) =>
|
||||||
true
|
true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
@@ -678,12 +678,12 @@ trait RepositoryService {
|
|||||||
|
|
||||||
def hasGuestRole(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
def hasGuestRole(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 (getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
case Some(a) if getGroupMembers(owner).exists(_.userName == a.userName) => true
|
||||||
case Some(a)
|
case Some(a)
|
||||||
if (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER, Role.GUEST))
|
if getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER, Role.GUEST))
|
||||||
.contains(a.userName)) =>
|
.contains(a.userName) =>
|
||||||
true
|
true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
@@ -694,17 +694,29 @@ trait RepositoryService {
|
|||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
loginAccount match {
|
loginAccount match {
|
||||||
case Some(x) if (x.isAdmin) => true
|
case Some(x) if x.isAdmin => true
|
||||||
case Some(x) if (repository.userName == x.userName) => true
|
case Some(x) if repository.userName == x.userName => true
|
||||||
case Some(x) if (getGroupMembers(repository.userName).exists(_.userName == x.userName)) => true
|
case Some(x) if getGroupMembers(repository.userName).exists(_.userName == x.userName) => true
|
||||||
case Some(x)
|
case Some(x) if getCollaboratorUserNames(repository.userName, repository.repositoryName).contains(x.userName) =>
|
||||||
if (getCollaboratorUserNames(repository.userName, repository.repositoryName).contains(x.userName)) =>
|
|
||||||
true
|
true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def isWritable(repository: Repository, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||||
|
loginAccount match {
|
||||||
|
case Some(x) if x.isAdmin => true
|
||||||
|
case Some(x) if repository.userName == x.userName => true
|
||||||
|
case Some(x) if getGroupMembers(repository.userName).exists(_.userName == x.userName) => true
|
||||||
|
case Some(x)
|
||||||
|
if getCollaboratorUserNames(repository.userName, repository.repositoryName, Seq(Role.ADMIN, Role.DEVELOPER))
|
||||||
|
.contains(x.userName) =>
|
||||||
|
true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def getForkedCount(userName: String, repositoryName: String)(implicit s: Session): Int =
|
private def getForkedCount(userName: String, repositoryName: String)(implicit s: Session): Int =
|
||||||
Query(Repositories.filter { t =>
|
Query(Repositories.filter { t =>
|
||||||
(t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
|
(t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import gitbucket.core.api.JsonFormat.Context
|
|||||||
import gitbucket.core.model.WebHook
|
import gitbucket.core.model.WebHook
|
||||||
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
||||||
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
||||||
import gitbucket.core.service.WebHookService._
|
import gitbucket.core.service.WebHookService.*
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service.*
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits.*
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util.*
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi.*
|
||||||
import gitbucket.core.model.activity.{
|
import gitbucket.core.model.activity.{
|
||||||
BaseActivityInfo,
|
BaseActivityInfo,
|
||||||
CloseIssueInfo,
|
CloseIssueInfo,
|
||||||
@@ -33,9 +33,9 @@ import gitbucket.core.servlet.Database
|
|||||||
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.http.server.GitServlet
|
import org.eclipse.jgit.http.server.GitServlet
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib.*
|
||||||
import org.eclipse.jgit.transport._
|
import org.eclipse.jgit.transport.*
|
||||||
import org.eclipse.jgit.transport.resolver._
|
import org.eclipse.jgit.transport.resolver.*
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import javax.servlet.ServletConfig
|
import javax.servlet.ServletConfig
|
||||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||||
@@ -43,7 +43,7 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
|||||||
import org.eclipse.jgit.internal.storage.file.FileRepository
|
import org.eclipse.jgit.internal.storage.file.FileRepository
|
||||||
import org.json4s.Formats
|
import org.json4s.Formats
|
||||||
import org.json4s.convertToJsonInput
|
import org.json4s.convertToJsonInput
|
||||||
import org.json4s.jackson.Serialization._
|
import org.json4s.jackson.Serialization.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Git repository via HTTP.
|
* Provides Git repository via HTTP.
|
||||||
@@ -117,7 +117,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
GitLfs.BatchResponseObject(
|
GitLfs.BatchResponseObject(
|
||||||
requestObject.oid,
|
requestObject.oid,
|
||||||
requestObject.size,
|
requestObject.size,
|
||||||
true,
|
authenticated = true,
|
||||||
GitLfs.Actions(
|
GitLfs.Actions(
|
||||||
upload = Some(
|
upload = Some(
|
||||||
GitLfs.Action(
|
GitLfs.Action(
|
||||||
@@ -138,7 +138,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
GitLfs.BatchResponseObject(
|
GitLfs.BatchResponseObject(
|
||||||
requestObject.oid,
|
requestObject.oid,
|
||||||
requestObject.size,
|
requestObject.size,
|
||||||
true,
|
authenticated = true,
|
||||||
GitLfs.Actions(
|
GitLfs.Actions(
|
||||||
download = Some(
|
download = Some(
|
||||||
GitLfs.Action(
|
GitLfs.Action(
|
||||||
@@ -223,7 +223,7 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters.*
|
||||||
|
|
||||||
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
|
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
|
||||||
extends PostReceiveHook
|
extends PostReceiveHook
|
||||||
@@ -242,6 +242,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
with WebHookPullRequestReviewCommentService
|
with WebHookPullRequestReviewCommentService
|
||||||
with CommitsService
|
with CommitsService
|
||||||
with SystemSettingsService
|
with SystemSettingsService
|
||||||
|
with ProtectedBranchService
|
||||||
with RequestCache {
|
with RequestCache {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||||
@@ -253,7 +254,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
commands.asScala.foreach { command =>
|
commands.asScala.foreach { command =>
|
||||||
// call pre-commit hook
|
// call pre-commit hook
|
||||||
PluginRegistry().getReceiveHooks
|
PluginRegistry().getReceiveHooks
|
||||||
.flatMap(_.preReceive(owner, repository, receivePack, command, pusher, false))
|
.flatMap(_.preReceive(owner, repository, receivePack, command, pusher, mergePullRequest = false))
|
||||||
.headOption
|
.headOption
|
||||||
.foreach { error =>
|
.foreach { error =>
|
||||||
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, error)
|
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, error)
|
||||||
@@ -428,8 +429,8 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
repositoryInfo,
|
repositoryInfo,
|
||||||
newCommits,
|
newCommits,
|
||||||
ownerAccount,
|
ownerAccount,
|
||||||
newId = command.getNewId(),
|
newId = command.getNewId,
|
||||||
oldId = command.getOldId()
|
oldId = command.getOldId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,7 +454,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
|
|
||||||
// call post-commit hook
|
// call post-commit hook
|
||||||
PluginRegistry().getReceiveHooks
|
PluginRegistry().getReceiveHooks
|
||||||
.foreach(_.postReceive(owner, repository, receivePack, command, pusher, false))
|
.foreach(_.postReceive(owner, repository, receivePack, command, pusher, mergePullRequest = false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// update repository last modified time.
|
// update repository last modified time.
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ trait OneselfAuthenticator { self: ControllerBase =>
|
|||||||
|
|
||||||
private def authenticate(action: => Any) = {
|
private def authenticate(action: => Any) = {
|
||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if (x.isAdmin) => action
|
case Some(x) if x.isAdmin => action
|
||||||
case Some(x) if (request.paths(0) == x.userName) => action
|
case Some(x) if request.paths(0) == x.userName => action
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ trait OneselfAuthenticator { self: ControllerBase =>
|
|||||||
* Allows only the repository owner and administrators.
|
* Allows only the repository owner and administrators.
|
||||||
*/
|
*/
|
||||||
trait OwnerAuthenticator { self: ControllerBase & RepositoryService & AccountService =>
|
trait OwnerAuthenticator { self: ControllerBase & RepositoryService & AccountService =>
|
||||||
protected def ownerOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def ownerOnly(action: RepositoryInfo => Any) = { authenticate(action) }
|
||||||
protected def ownerOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def ownerOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
private def authenticate(action: (RepositoryInfo) => Any) = {
|
private def authenticate(action: (RepositoryInfo) => Any) = {
|
||||||
@@ -34,14 +34,14 @@ trait OwnerAuthenticator { self: ControllerBase & RepositoryService & AccountSer
|
|||||||
val repoName = params("repository")
|
val repoName = params("repository")
|
||||||
getRepository(userName, repoName).map { repository =>
|
getRepository(userName, repoName).map { repository =>
|
||||||
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)
|
||||||
// TODO Repository management is allowed for only group managers?
|
// TODO Repository management is allowed for only group managers?
|
||||||
case Some(x) if (getGroupMembers(repository.owner).exists { m =>
|
case Some(x) if getGroupMembers(repository.owner).exists { m =>
|
||||||
m.userName == x.userName && m.isManager
|
m.userName == x.userName && m.isManager
|
||||||
}) =>
|
} =>
|
||||||
action(repository)
|
action(repository)
|
||||||
case Some(x) if (getCollaboratorUserNames(userName, repoName, Seq(Role.ADMIN)).contains(x.userName)) =>
|
case Some(x) if getCollaboratorUserNames(userName, repoName, Seq(Role.ADMIN)).contains(x.userName) =>
|
||||||
action(repository)
|
action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
@@ -83,10 +83,10 @@ trait AdminAuthenticator { self: ControllerBase =>
|
|||||||
* Allows only guests and signed in users who can access the repository.
|
* Allows only guests and signed in users who can access the repository.
|
||||||
*/
|
*/
|
||||||
trait ReferrerAuthenticator { self: ControllerBase & RepositoryService & AccountService =>
|
trait ReferrerAuthenticator { self: ControllerBase & RepositoryService & 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, _)) }
|
||||||
|
|
||||||
private def authenticate(action: (RepositoryInfo) => Any) = {
|
private def authenticate(action: RepositoryInfo => Any) = {
|
||||||
val userName = params("owner")
|
val userName = params("owner")
|
||||||
val repoName = params("repository")
|
val repoName = params("repository")
|
||||||
getRepository(userName, repoName).map { repository =>
|
getRepository(userName, repoName).map { repository =>
|
||||||
@@ -103,12 +103,12 @@ trait ReferrerAuthenticator { self: ControllerBase & RepositoryService & Account
|
|||||||
* Allows only signed in users who have read permission for the repository.
|
* Allows only signed in users who have read permission for the repository.
|
||||||
*/
|
*/
|
||||||
trait ReadableUsersAuthenticator { self: ControllerBase & RepositoryService & AccountService =>
|
trait ReadableUsersAuthenticator { self: ControllerBase & RepositoryService & 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) => {
|
protected def readableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => {
|
||||||
authenticate(action(form, _))
|
authenticate(action(form, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def authenticate(action: (RepositoryInfo) => Any) = {
|
private def authenticate(action: RepositoryInfo => Any) = {
|
||||||
val userName = params("owner")
|
val userName = params("owner")
|
||||||
val repoName = params("repository")
|
val repoName = params("repository")
|
||||||
getRepository(userName, repoName).map { repository =>
|
getRepository(userName, repoName).map { repository =>
|
||||||
@@ -125,24 +125,19 @@ trait ReadableUsersAuthenticator { self: ControllerBase & RepositoryService & Ac
|
|||||||
* Allows only signed in users who have write permission for the repository.
|
* Allows only signed in users who have write permission for the repository.
|
||||||
*/
|
*/
|
||||||
trait WritableUsersAuthenticator { self: ControllerBase & RepositoryService & AccountService =>
|
trait WritableUsersAuthenticator { self: ControllerBase & RepositoryService & AccountService =>
|
||||||
protected def writableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def writableUsersOnly(action: RepositoryInfo => Any) = { authenticate(action) }
|
||||||
protected def writableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => {
|
protected def writableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => {
|
||||||
authenticate(action(form, _))
|
authenticate(action(form, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def authenticate(action: (RepositoryInfo) => Any) = {
|
private def authenticate(action: RepositoryInfo => Any) = {
|
||||||
val userName = params("owner")
|
val userName = params("owner")
|
||||||
val repoName = params("repository")
|
val repoName = params("repository")
|
||||||
getRepository(userName, repoName).map { repository =>
|
getRepository(userName, repoName).map { repository =>
|
||||||
context.loginAccount match {
|
if (isWritable(repository.repository, context.loginAccount)) {
|
||||||
case Some(x) if (x.isAdmin) => action(repository)
|
action(repository)
|
||||||
case Some(x) if (userName == x.userName) => action(repository)
|
} else {
|
||||||
case Some(x) if (getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
|
Unauthorized()
|
||||||
case Some(x)
|
|
||||||
if (getCollaboratorUserNames(userName, repoName, Seq(Role.ADMIN, Role.DEVELOPER))
|
|
||||||
.contains(x.userName)) =>
|
|
||||||
action(repository)
|
|
||||||
case _ => Unauthorized()
|
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
}
|
}
|
||||||
@@ -159,9 +154,9 @@ trait GroupManagerAuthenticator { self: ControllerBase & AccountService =>
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if x.isAdmin => action
|
case Some(x) if x.isAdmin => action
|
||||||
case Some(x) if x.userName == request.paths(0) => action
|
case Some(x) if x.userName == request.paths(0) => action
|
||||||
case Some(x) if (getGroupMembers(request.paths(0)).exists { member =>
|
case Some(x) if getGroupMembers(request.paths(0)).exists { member =>
|
||||||
member.userName == x.userName && member.isManager
|
member.userName == x.userName && member.isManager
|
||||||
}) =>
|
} =>
|
||||||
action
|
action
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||||
branch: String,
|
branch: String,
|
||||||
protection: gitbucket.core.api.ApiBranchProtection,
|
protection: gitbucket.core.api.ApiBranchProtectionResponse,
|
||||||
knownContexts: Seq[String],
|
knownContexts: Seq[String],
|
||||||
info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
@@ -22,9 +22,44 @@
|
|||||||
</label>
|
</label>
|
||||||
<p class="help-block">Disables force-pushes to this branch and prevents it from being deleted.</p>
|
<p class="help-block">Disables force-pushes to this branch and prevents it from being deleted.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--====================================================================-->
|
||||||
|
<!-- Enforce administrators -->
|
||||||
|
<!--====================================================================-->
|
||||||
<div class="checkbox js-enabled" style="display:none">
|
<div class="checkbox js-enabled" style="display:none">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="has_required_statuses" onclick="update()" @check(protection.status.enforcement_level.name!="off") @if(knownContexts.isEmpty){disabled }>
|
<input type="checkbox" name="enforce_for_admins" onclick="update()" @check(protection.enforce_admins.exists(_.enabled))>
|
||||||
|
<span class="strong">Include administrators</span>
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Enforce restrictions even for repository administrators.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--====================================================================-->
|
||||||
|
<!-- Push restrictions -->
|
||||||
|
<!--====================================================================-->
|
||||||
|
<div class="checkbox js-enabled" style="display:none">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="restrictions" onclick="update()" @check(protection.restrictions.isDefined)>
|
||||||
|
<span class="strong">Restrict users for push</span>
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Restrict users who can push to this branch</p>
|
||||||
|
<div class="js-restrictions_enabled" style="display: none;">
|
||||||
|
<ul id="restrictions-user-list">
|
||||||
|
</ul>
|
||||||
|
@gitbucket.core.helper.html.account("userName-restrictions-user", 200, true, false)
|
||||||
|
<input type="button" class="btn btn-default add-restrictions-user" value="Add"/>
|
||||||
|
<div>
|
||||||
|
<span class="error" id="error-restrictions-user"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--====================================================================-->
|
||||||
|
<!-- Status check -->
|
||||||
|
<!--====================================================================-->
|
||||||
|
<div class="checkbox js-enabled" style="display:none">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="has_required_statuses" onclick="update()" @check(protection.required_status_checks.isDefined) @if(knownContexts.isEmpty){disabled }>
|
||||||
<span class="strong">Require status checks to pass before merging</span>
|
<span class="strong">Require status checks to pass before merging</span>
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">When enabled, commits must first be pushed to another branch, then merged or pushed directly to <b>@branch</b> after status checks have passed.</p>
|
<p class="help-block">When enabled, commits must first be pushed to another branch, then merged or pushed directly to <b>@branch</b> after status checks have passed.</p>
|
||||||
@@ -48,14 +83,6 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="enforce_for_admins" onclick="update()" @check(protection.status.enforcement_level.name=="everyone")>
|
|
||||||
<span class="strong">Include administrators</span>
|
|
||||||
</label>
|
|
||||||
<p class="help-block">Enforce required status checks for repository administrators.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -68,59 +95,114 @@
|
|||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
function getValue(){
|
function getValue(){
|
||||||
var v = {}, contexts=[];
|
const v = {}, contexts = [];
|
||||||
|
let restrictions = undefined;
|
||||||
$("input[type=checkbox]:checked").each(function(){
|
$("input[type=checkbox]:checked").each(function(){
|
||||||
if(this.name === 'contexts'){
|
if(this.name === 'contexts') {
|
||||||
contexts.push(this.value);
|
contexts.push(this.value);
|
||||||
|
} else if (this.name === 'restrictions') {
|
||||||
|
restrictions = $('#restrictions-user-list li').map(function(i, e){
|
||||||
|
return $(e).data('name');
|
||||||
|
}).get();
|
||||||
} else {
|
} else {
|
||||||
v[this.name] = true;
|
v[this.name] = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(v.enabled){
|
if(v.enabled){
|
||||||
return {
|
return {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
required_status_checks: {
|
enforce_admins: v.enforce_for_admins,
|
||||||
enforcement_level: v.has_required_statuses ? ((v.enforce_for_admins ? 'everyone' : 'non_admins')) : 'off',
|
required_status_checks: v.has_required_statuses ? { contexts: contexts } : undefined,
|
||||||
contexts: v.has_required_statuses ? contexts : []
|
restrictions: restrictions ? { users: restrictions } : undefined
|
||||||
}
|
};
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
enabled: false,
|
enabled: false
|
||||||
required_status_checks: {
|
|
||||||
enforcement_level: "off",
|
|
||||||
contexts: []
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateView(protection){
|
function updateView(protection){
|
||||||
$('.js-enabled').toggle(protection.enabled);
|
$('.js-enabled').toggle(protection.enabled);
|
||||||
$('.js-has_required_statuses').toggle(protection.required_status_checks.enforcement_level != 'off');
|
$('.js-restrictions_enabled').toggle(protection.restrictions !== undefined);
|
||||||
$('.js-submit-btn').attr('disabled',protection.required_status_checks.enforcement_level != 'off' && protection.required_status_checks.contexts.length == 0);
|
$('.js-has_required_statuses').toggle(protection.required_status_checks !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(){
|
function update(){
|
||||||
var protection = getValue();
|
const protection = getValue();
|
||||||
updateView(protection);
|
updateView(protection);
|
||||||
}
|
}
|
||||||
$(update);
|
|
||||||
function submitForm(e){
|
function submitForm(e){
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var protection = getValue();
|
const protection = getValue();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method:'PATCH',
|
method: 'PATCH',
|
||||||
url:'@context.path/api/v3/repos/@repository.owner/@repository.name/branches/@helpers.urlEncode(branch)',
|
url: '@context.path/api/v3/repos/@repository.owner/@repository.name/branches/@helpers.urlEncode(branch)',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
data:JSON.stringify({protection:protection}),
|
data: JSON.stringify({protection: protection}),
|
||||||
success:function(r){
|
success: function(r){
|
||||||
$('#saved-info').show();
|
$('#saved-info').show();
|
||||||
},
|
},
|
||||||
error:function(err){
|
error: function(err){
|
||||||
console.log(err);
|
console.log(err);
|
||||||
alert('update error');
|
alert('update error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addUserToListHTML(userName, id){
|
||||||
|
$(id).append($('<li>').data('name', userName)
|
||||||
|
.append(' ')
|
||||||
|
.append(userName)
|
||||||
|
.append($('<a href="#" onclick="$(this).parent().remove();" class="remove">(remove)</a>')));
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
// Initialize
|
||||||
|
update();
|
||||||
|
|
||||||
|
@protection.restrictions.map(_.users).map { users =>
|
||||||
|
@users.map { user =>
|
||||||
|
addUserToListHTML('@user', '#restrictions-user-list');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.add-restrictions-user').click(function(){
|
||||||
|
$('#error-restrictions-user').text('');
|
||||||
|
const userName = $('#userName-restrictions-user').val();
|
||||||
|
|
||||||
|
// check empty
|
||||||
|
if($.trim(userName) === ''){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check duplication
|
||||||
|
const exists = $('#restrictions-user-list li').filter(function(){
|
||||||
|
return $(this).data('name') === userName;
|
||||||
|
}).length > 0;
|
||||||
|
if(exists){
|
||||||
|
$('#error-restrictions-user').text('User has been already added.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check existence
|
||||||
|
$.post('@context.path/_user/existence', {
|
||||||
|
'userName': userName,
|
||||||
|
'owner': '@repository.owner',
|
||||||
|
'repository': '@repository.name'
|
||||||
|
},
|
||||||
|
function(data, status){
|
||||||
|
if(data !== ''){
|
||||||
|
addUserToListHTML(userName, '#restrictions-user-list');
|
||||||
|
$('#userName-restrictions-user').val('');
|
||||||
|
} else {
|
||||||
|
$('#error-restrictions-user').text("User does not exist or isn't writable to this repository.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package gitbucket.core.api
|
package gitbucket.core.api
|
||||||
|
|
||||||
import java.util.{Calendar, Date, TimeZone}
|
import gitbucket.core.api.ApiBranchProtectionResponse.Restrictions
|
||||||
|
|
||||||
import gitbucket.core.model._
|
import java.util.{Calendar, Date, TimeZone}
|
||||||
|
import gitbucket.core.model.*
|
||||||
import gitbucket.core.plugin.PluginInfo
|
import gitbucket.core.plugin.PluginInfo
|
||||||
import gitbucket.core.service.ProtectedBranchService.ProtectedBranchInfo
|
import gitbucket.core.service.ProtectedBranchService.ProtectedBranchInfo
|
||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
@@ -15,7 +16,7 @@ object ApiSpecModels {
|
|||||||
|
|
||||||
implicit val context: JsonFormat.Context = JsonFormat.Context("http://gitbucket.exmple.com", None)
|
implicit val context: JsonFormat.Context = JsonFormat.Context("http://gitbucket.exmple.com", None)
|
||||||
|
|
||||||
val date1 = {
|
val date1: Date = {
|
||||||
val d = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
val d = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||||
d.set(2011, 3, 14, 16, 0, 49)
|
d.set(2011, 3, 14, 16, 0, 49)
|
||||||
d.getTime
|
d.getTime
|
||||||
@@ -29,7 +30,7 @@ object ApiSpecModels {
|
|||||||
|
|
||||||
// Models
|
// Models
|
||||||
|
|
||||||
val account = Account(
|
val account: Account = Account(
|
||||||
userName = "octocat",
|
userName = "octocat",
|
||||||
fullName = "octocat",
|
fullName = "octocat",
|
||||||
mailAddress = "octocat@example.com",
|
mailAddress = "octocat@example.com",
|
||||||
@@ -45,10 +46,10 @@ object ApiSpecModels {
|
|||||||
description = None
|
description = None
|
||||||
)
|
)
|
||||||
|
|
||||||
val sha1 = "6dcb09b5b57875f334f61aebed695e2e4193db5e"
|
val sha1: String = "6dcb09b5b57875f334f61aebed695e2e4193db5e"
|
||||||
val repo1Name = RepositoryName("octocat/Hello-World")
|
val repo1Name: RepositoryName = RepositoryName("octocat/Hello-World")
|
||||||
|
|
||||||
val repository = Repository(
|
val repository: Repository = Repository(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
isPrivate = false,
|
isPrivate = false,
|
||||||
@@ -73,7 +74,7 @@ object ApiSpecModels {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val repositoryInfo = RepositoryInfo(
|
val repositoryInfo: RepositoryInfo = RepositoryInfo(
|
||||||
owner = repo1Name.owner,
|
owner = repo1Name.owner,
|
||||||
name = repo1Name.name,
|
name = repo1Name.name,
|
||||||
repository = repository,
|
repository = repository,
|
||||||
@@ -101,7 +102,7 @@ object ApiSpecModels {
|
|||||||
managers = Seq("myboss")
|
managers = Seq("myboss")
|
||||||
)
|
)
|
||||||
|
|
||||||
val label = Label(
|
val label: Label = Label(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
labelId = 10,
|
labelId = 10,
|
||||||
@@ -109,7 +110,7 @@ object ApiSpecModels {
|
|||||||
color = "f29513"
|
color = "f29513"
|
||||||
)
|
)
|
||||||
|
|
||||||
val issue = Issue(
|
val issue: Issue = Issue(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
issueId = 1347,
|
issueId = 1347,
|
||||||
@@ -124,14 +125,14 @@ object ApiSpecModels {
|
|||||||
isPullRequest = false
|
isPullRequest = false
|
||||||
)
|
)
|
||||||
|
|
||||||
val issuePR = issue.copy(
|
val issuePR: Issue = issue.copy(
|
||||||
title = "new-feature",
|
title = "new-feature",
|
||||||
content = Some("Please pull these awesome changes"),
|
content = Some("Please pull these awesome changes"),
|
||||||
closed = true,
|
closed = true,
|
||||||
isPullRequest = true
|
isPullRequest = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val issueComment = IssueComment(
|
val issueComment: IssueComment = IssueComment(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
issueId = issue.issueId,
|
issueId = issue.issueId,
|
||||||
@@ -143,7 +144,7 @@ object ApiSpecModels {
|
|||||||
updatedDate = date1
|
updatedDate = date1
|
||||||
)
|
)
|
||||||
|
|
||||||
val pullRequest = PullRequest(
|
val pullRequest: PullRequest = PullRequest(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
issueId = issuePR.issueId,
|
issueId = issuePR.issueId,
|
||||||
@@ -156,7 +157,7 @@ object ApiSpecModels {
|
|||||||
isDraft = true
|
isDraft = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val commitComment = CommitComment(
|
val commitComment: CommitComment = CommitComment(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
commitId = sha1,
|
commitId = sha1,
|
||||||
@@ -174,7 +175,7 @@ object ApiSpecModels {
|
|||||||
originalNewLine = None
|
originalNewLine = None
|
||||||
)
|
)
|
||||||
|
|
||||||
val commitStatus = CommitStatus(
|
val commitStatus: CommitStatus = CommitStatus(
|
||||||
commitStatusId = 1,
|
commitStatusId = 1,
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
@@ -188,7 +189,7 @@ object ApiSpecModels {
|
|||||||
updatedDate = date1
|
updatedDate = date1
|
||||||
)
|
)
|
||||||
|
|
||||||
val milestone = Milestone(
|
val milestone: Milestone = Milestone(
|
||||||
userName = repo1Name.owner,
|
userName = repo1Name.owner,
|
||||||
repositoryName = repo1Name.name,
|
repositoryName = repo1Name.name,
|
||||||
milestoneId = 1,
|
milestoneId = 1,
|
||||||
@@ -200,28 +201,28 @@ object ApiSpecModels {
|
|||||||
|
|
||||||
// APIs
|
// APIs
|
||||||
|
|
||||||
val apiUser = ApiUser(account)
|
val apiUser: ApiUser = ApiUser(account)
|
||||||
|
|
||||||
val apiRepository = ApiRepository(
|
val apiRepository: ApiRepository = ApiRepository(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
owner = apiUser,
|
owner = apiUser,
|
||||||
forkedCount = repositoryInfo.forkedCount,
|
forkedCount = repositoryInfo.forkedCount,
|
||||||
watchers = 0
|
watchers = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiLabel = ApiLabel(
|
val apiLabel: ApiLabel = ApiLabel(
|
||||||
label = label,
|
label = label,
|
||||||
repositoryName = repo1Name
|
repositoryName = repo1Name
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiMilestone = ApiMilestone(
|
val apiMilestone: ApiMilestone = ApiMilestone(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
milestone = milestone,
|
milestone = milestone,
|
||||||
open_issue_count = 1,
|
open_issue_count = 1,
|
||||||
closed_issue_count = 1
|
closed_issue_count = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiIssue = ApiIssue(
|
val apiIssue: ApiIssue = ApiIssue(
|
||||||
issue = issue,
|
issue = issue,
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
user = apiUser,
|
user = apiUser,
|
||||||
@@ -230,7 +231,7 @@ object ApiSpecModels {
|
|||||||
milestone = Some(apiMilestone)
|
milestone = Some(apiMilestone)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiNotAssignedIssue = ApiIssue(
|
val apiNotAssignedIssue: ApiIssue = ApiIssue(
|
||||||
issue = issue,
|
issue = issue,
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
user = apiUser,
|
user = apiUser,
|
||||||
@@ -239,7 +240,7 @@ object ApiSpecModels {
|
|||||||
milestone = Some(apiMilestone)
|
milestone = Some(apiMilestone)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiIssuePR = ApiIssue(
|
val apiIssuePR: ApiIssue = ApiIssue(
|
||||||
issue = issuePR,
|
issue = issuePR,
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
user = apiUser,
|
user = apiUser,
|
||||||
@@ -248,7 +249,7 @@ object ApiSpecModels {
|
|||||||
milestone = Some(apiMilestone)
|
milestone = Some(apiMilestone)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiComment = ApiComment(
|
val apiComment: ApiComment = ApiComment(
|
||||||
comment = issueComment,
|
comment = issueComment,
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
issueId = issueComment.issueId,
|
issueId = issueComment.issueId,
|
||||||
@@ -256,7 +257,7 @@ object ApiSpecModels {
|
|||||||
isPullRequest = false
|
isPullRequest = false
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiCommentPR = ApiComment(
|
val apiCommentPR: ApiComment = ApiComment(
|
||||||
comment = issueComment,
|
comment = issueComment,
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
issueId = issueComment.issueId,
|
issueId = issueComment.issueId,
|
||||||
@@ -264,7 +265,7 @@ object ApiSpecModels {
|
|||||||
isPullRequest = true
|
isPullRequest = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiPullRequest = ApiPullRequest(
|
val apiPullRequest: ApiPullRequest = ApiPullRequest(
|
||||||
issue = issuePR,
|
issue = issuePR,
|
||||||
pullRequest = pullRequest,
|
pullRequest = pullRequest,
|
||||||
headRepo = apiRepository,
|
headRepo = apiRepository,
|
||||||
@@ -275,15 +276,14 @@ object ApiSpecModels {
|
|||||||
mergedComment = Some((issueComment, account))
|
mergedComment = Some((issueComment, account))
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
|
val apiPullRequestReviewComment: ApiPullRequestReviewComment = ApiPullRequestReviewComment(
|
||||||
val apiPullRequestReviewComment = ApiPullRequestReviewComment(
|
|
||||||
comment = commitComment,
|
comment = commitComment,
|
||||||
commentedUser = apiUser,
|
commentedUser = apiUser,
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
issueId = commitComment.issueId.get
|
issueId = commitComment.issueId.get
|
||||||
)
|
)
|
||||||
|
|
||||||
val commitInfo = (id: String) =>
|
val commitInfo: String => CommitInfo = (id: String) =>
|
||||||
CommitInfo(
|
CommitInfo(
|
||||||
id = id,
|
id = id,
|
||||||
shortMessage = "short message",
|
shortMessage = "short message",
|
||||||
@@ -299,12 +299,12 @@ object ApiSpecModels {
|
|||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiCommitListItem = ApiCommitListItem(
|
val apiCommitListItem: ApiCommitListItem = ApiCommitListItem(
|
||||||
commit = commitInfo(sha1),
|
commit = commitInfo(sha1),
|
||||||
repositoryName = repo1Name
|
repositoryName = repo1Name
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiCommit = {
|
val apiCommit: ApiCommit = {
|
||||||
val commit = commitInfo(sha1)
|
val commit = commitInfo(sha1)
|
||||||
ApiCommit(
|
ApiCommit(
|
||||||
id = commit.id,
|
id = commit.id,
|
||||||
@@ -318,7 +318,7 @@ object ApiSpecModels {
|
|||||||
)(repo1Name)
|
)(repo1Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
val apiCommits = ApiCommits(
|
val apiCommits: ApiCommits = ApiCommits(
|
||||||
repositoryName = repo1Name,
|
repositoryName = repo1Name,
|
||||||
commitInfo = commitInfo(sha1),
|
commitInfo = commitInfo(sha1),
|
||||||
diffs = Seq(
|
diffs = Seq(
|
||||||
@@ -348,42 +348,45 @@ object ApiSpecModels {
|
|||||||
commentCount = 2
|
commentCount = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiCommitStatus = ApiCommitStatus(
|
val apiCommitStatus: ApiCommitStatus = ApiCommitStatus(
|
||||||
status = commitStatus,
|
status = commitStatus,
|
||||||
creator = apiUser
|
creator = apiUser
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiCombinedCommitStatus = ApiCombinedCommitStatus(
|
val apiCombinedCommitStatus: ApiCombinedCommitStatus = ApiCombinedCommitStatus(
|
||||||
sha = sha1,
|
sha = sha1,
|
||||||
statuses = Iterable((commitStatus, account)),
|
statuses = Iterable((commitStatus, account)),
|
||||||
repository = apiRepository
|
repository = apiRepository
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiBranchProtectionOutput = ApiBranchProtection(
|
val apiBranchProtectionOutput: ApiBranchProtectionResponse = ApiBranchProtectionResponse(
|
||||||
info = ProtectedBranchInfo(
|
info = ProtectedBranchInfo(
|
||||||
owner = repo1Name.owner,
|
owner = repo1Name.owner,
|
||||||
repository = repo1Name.name,
|
repository = repo1Name.name,
|
||||||
branch = "main",
|
branch = "main",
|
||||||
enabled = true,
|
enabled = true,
|
||||||
contexts = Seq("continuous-integration/travis-ci"),
|
contexts = Some(Seq("continuous-integration/travis-ci")),
|
||||||
includeAdministrators = true
|
enforceAdmins = true,
|
||||||
|
restrictionsUsers = Some(Seq("admin"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiBranchProtectionInput = new ApiBranchProtection(
|
val apiBranchProtectionInput: ApiBranchProtectionResponse = new ApiBranchProtectionResponse(
|
||||||
url = None,
|
url = None,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
required_status_checks = Some(
|
required_status_checks = Some(
|
||||||
ApiBranchProtection.Status(
|
ApiBranchProtectionResponse.Status(
|
||||||
url = None,
|
url = None,
|
||||||
enforcement_level = ApiBranchProtection.Everyone,
|
enforcement_level = ApiBranchProtectionResponse.Everyone,
|
||||||
contexts = Seq("continuous-integration/travis-ci"),
|
contexts = Seq("continuous-integration/travis-ci"),
|
||||||
contexts_url = None
|
contexts_url = None
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
restrictions = Some(Restrictions(users = Seq("admin"))),
|
||||||
|
enforce_admins = None
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiBranch = ApiBranch(
|
val apiBranch: ApiBranch = ApiBranch(
|
||||||
name = "main",
|
name = "main",
|
||||||
commit = ApiBranchCommit(sha1),
|
commit = ApiBranchCommit(sha1),
|
||||||
protection = apiBranchProtectionOutput
|
protection = apiBranchProtectionOutput
|
||||||
@@ -391,12 +394,12 @@ object ApiSpecModels {
|
|||||||
repositoryName = repo1Name
|
repositoryName = repo1Name
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiBranchForList = ApiBranchForList(
|
val apiBranchForList: ApiBranchForList = ApiBranchForList(
|
||||||
name = "main",
|
name = "main",
|
||||||
commit = ApiBranchCommit(sha1)
|
commit = ApiBranchCommit(sha1)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiContents = ApiContents(
|
val apiContents: ApiContents = ApiContents(
|
||||||
fileInfo = FileInfo(
|
fileInfo = FileInfo(
|
||||||
id = ObjectId.fromString(sha1),
|
id = ObjectId.fromString(sha1),
|
||||||
isDirectory = false,
|
isDirectory = false,
|
||||||
@@ -413,14 +416,14 @@ object ApiSpecModels {
|
|||||||
content = Some("README".getBytes("UTF-8"))
|
content = Some("README".getBytes("UTF-8"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiEndPoint = ApiEndPoint()
|
val apiEndPoint: ApiEndPoint = ApiEndPoint()
|
||||||
|
|
||||||
val apiError = ApiError(
|
val apiError: ApiError = ApiError(
|
||||||
message = "A repository with this name already exists on this account",
|
message = "A repository with this name already exists on this account",
|
||||||
documentation_url = Some("https://developer.github.com/v3/repos/#create")
|
documentation_url = Some("https://developer.github.com/v3/repos/#create")
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiGroup = ApiGroup(
|
val apiGroup: ApiGroup = ApiGroup(
|
||||||
account.copy(
|
account.copy(
|
||||||
isAdmin = true,
|
isAdmin = true,
|
||||||
isGroupAccount = true,
|
isGroupAccount = true,
|
||||||
@@ -428,7 +431,7 @@ object ApiSpecModels {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiPlugin = ApiPlugin(
|
val apiPlugin: ApiPlugin = ApiPlugin(
|
||||||
plugin = PluginInfo(
|
plugin = PluginInfo(
|
||||||
pluginId = "gist",
|
pluginId = "gist",
|
||||||
pluginName = "Gist Plugin",
|
pluginName = "Gist Plugin",
|
||||||
@@ -441,12 +444,12 @@ object ApiSpecModels {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiPusher = ApiPusher(account)
|
val apiPusher: ApiPusher = ApiPusher(account)
|
||||||
|
|
||||||
// have both urls as https, as the expected samples are using https
|
// have both urls as https, as the expected samples are using https
|
||||||
val gitHubContext = JsonFormat.Context("https://api.github.com", Some("https://api.github.com"))
|
val gitHubContext: JsonFormat.Context = JsonFormat.Context("https://api.github.com", Some("https://api.github.com"))
|
||||||
|
|
||||||
val apiRefHeadsMaster = ApiRef(
|
val apiRefHeadsMaster: ApiRef = ApiRef(
|
||||||
ref = "refs/heads/main",
|
ref = "refs/heads/main",
|
||||||
url = ApiPath("/repos/gitbucket/gitbucket/git/refs/heads/main"),
|
url = ApiPath("/repos/gitbucket/gitbucket/git/refs/heads/main"),
|
||||||
node_id = "MDM6UmVmOTM1MDc0NjpyZWZzL2hlYWRzL21hc3Rlcg==",
|
node_id = "MDM6UmVmOTM1MDc0NjpyZWZzL2hlYWRzL21hc3Rlcg==",
|
||||||
@@ -457,7 +460,7 @@ object ApiSpecModels {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiRefTag = ApiRef(
|
val apiRefTag: ApiRef = ApiRef(
|
||||||
ref = "refs/tags/1.0",
|
ref = "refs/tags/1.0",
|
||||||
url = ApiPath("/repos/gitbucket/gitbucket/git/refs/tags/1.0"),
|
url = ApiPath("/repos/gitbucket/gitbucket/git/refs/tags/1.0"),
|
||||||
node_id = "MDM6UmVmOTM1MDc0NjpyZWZzL3RhZ3MvMS4w",
|
node_id = "MDM6UmVmOTM1MDc0NjpyZWZzL3RhZ3MvMS4w",
|
||||||
@@ -468,9 +471,9 @@ object ApiSpecModels {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val assetFileName = "010203040a0b0c0d"
|
val assetFileName: String = "010203040a0b0c0d"
|
||||||
|
|
||||||
val apiReleaseAsset = ApiReleaseAsset(
|
val apiReleaseAsset: ApiReleaseAsset = ApiReleaseAsset(
|
||||||
name = "release.zip",
|
name = "release.zip",
|
||||||
size = 100
|
size = 100
|
||||||
)(
|
)(
|
||||||
@@ -479,7 +482,7 @@ object ApiSpecModels {
|
|||||||
repositoryName = repo1Name
|
repositoryName = repo1Name
|
||||||
)
|
)
|
||||||
|
|
||||||
val apiRelease = ApiRelease(
|
val apiRelease: ApiRelease = ApiRelease(
|
||||||
name = "release1",
|
name = "release1",
|
||||||
tag_name = "tag1",
|
tag_name = "tag1",
|
||||||
body = Some("content"),
|
body = Some("content"),
|
||||||
@@ -489,7 +492,7 @@ object ApiSpecModels {
|
|||||||
|
|
||||||
// JSON String for APIs
|
// JSON String for APIs
|
||||||
|
|
||||||
val jsonUser = """{
|
val jsonUser: String = """{
|
||||||
|"login":"octocat",
|
|"login":"octocat",
|
||||||
|"email":"octocat@example.com",
|
|"email":"octocat@example.com",
|
||||||
|"type":"User",
|
|"type":"User",
|
||||||
@@ -501,7 +504,7 @@ object ApiSpecModels {
|
|||||||
|"avatar_url":"http://gitbucket.exmple.com/octocat/_avatar"
|
|"avatar_url":"http://gitbucket.exmple.com/octocat/_avatar"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonRepository = s"""{
|
val jsonRepository: String = s"""{
|
||||||
|"name":"Hello-World",
|
|"name":"Hello-World",
|
||||||
|"full_name":"octocat/Hello-World",
|
|"full_name":"octocat/Hello-World",
|
||||||
|"description":"This your first repo!",
|
|"description":"This your first repo!",
|
||||||
@@ -519,10 +522,10 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World"
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonLabel =
|
val jsonLabel: String =
|
||||||
"""{"name":"bug","color":"f29513","url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/labels/bug"}"""
|
"""{"name":"bug","color":"f29513","url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/labels/bug"}"""
|
||||||
|
|
||||||
val jsonMilestone = """{
|
val jsonMilestone: String = """{
|
||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/milestones/1",
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/milestones/1",
|
||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/milestone/1",
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/milestone/1",
|
||||||
|"id":1,
|
|"id":1,
|
||||||
@@ -535,7 +538,7 @@ object ApiSpecModels {
|
|||||||
|"due_on":"2011-04-14T16:00:49Z"
|
|"due_on":"2011-04-14T16:00:49Z"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonIssue = s"""{
|
val jsonIssue: String = s"""{
|
||||||
|"number":1347,
|
|"number":1347,
|
||||||
|"title":"Found a bug",
|
|"title":"Found a bug",
|
||||||
|"user":$jsonUser,
|
|"user":$jsonUser,
|
||||||
@@ -552,7 +555,7 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347"
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonNotAssignedIssue = s"""{
|
val jsonNotAssignedIssue: String = s"""{
|
||||||
|"number":1347,
|
|"number":1347,
|
||||||
|"title":"Found a bug",
|
|"title":"Found a bug",
|
||||||
|"user":$jsonUser,
|
|"user":$jsonUser,
|
||||||
@@ -568,7 +571,7 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347"
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonIssuePR = s"""{
|
val jsonIssuePR: String = s"""{
|
||||||
|"number":1347,
|
|"number":1347,
|
||||||
|"title":"new-feature",
|
|"title":"new-feature",
|
||||||
|"user":$jsonUser,
|
|"user":$jsonUser,
|
||||||
@@ -588,7 +591,7 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347"}
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347"}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonPullRequest = s"""{
|
val jsonPullRequest: String = s"""{
|
||||||
|"number":1347,
|
|"number":1347,
|
||||||
|"state":"closed",
|
|"state":"closed",
|
||||||
|"updated_at":"2011-04-14T16:00:49Z",
|
|"updated_at":"2011-04-14T16:00:49Z",
|
||||||
@@ -615,7 +618,7 @@ object ApiSpecModels {
|
|||||||
|"statuses_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"
|
|"statuses_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonPullRequestReviewComment = s"""{
|
val jsonPullRequestReviewComment: String = s"""{
|
||||||
|"id":29724692,
|
|"id":29724692,
|
||||||
|"path":"README.md",
|
|"path":"README.md",
|
||||||
|"commit_id":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
|"commit_id":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
||||||
@@ -632,7 +635,7 @@ object ApiSpecModels {
|
|||||||
|"pull_request":{"href":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347"}}
|
|"pull_request":{"href":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347"}}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonComment = s"""{
|
val jsonComment: String = s"""{
|
||||||
|"id":1,
|
|"id":1,
|
||||||
|"user":$jsonUser,
|
|"user":$jsonUser,
|
||||||
|"body":"Me too",
|
|"body":"Me too",
|
||||||
@@ -641,7 +644,7 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347#comment-1"
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347#comment-1"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonCommentPR = s"""{
|
val jsonCommentPR: String = s"""{
|
||||||
|"id":1,
|
|"id":1,
|
||||||
|"user":$jsonUser,
|
|"user":$jsonUser,
|
||||||
|"body":"Me too",
|
|"body":"Me too",
|
||||||
@@ -650,7 +653,7 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347#comment-1"
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347#comment-1"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonCommitListItem = s"""{
|
val jsonCommitListItem: String = s"""{
|
||||||
|"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
|"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
||||||
|"commit":{
|
|"commit":{
|
||||||
|"message":"full message",
|
|"message":"full message",
|
||||||
@@ -664,7 +667,7 @@ object ApiSpecModels {
|
|||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e"
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonCommit = (id: String) => s"""{
|
val jsonCommit: String => String = (id: String) => s"""{
|
||||||
|"id":"$id",
|
|"id":"$id",
|
||||||
|"message":"full message",
|
|"message":"full message",
|
||||||
|"timestamp":"2011-04-14T16:00:49Z",
|
|"timestamp":"2011-04-14T16:00:49Z",
|
||||||
@@ -677,7 +680,7 @@ object ApiSpecModels {
|
|||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/commit/$id"
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/commit/$id"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonCommits = s"""{
|
val jsonCommits: String = s"""{
|
||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
||||||
|"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
|"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
||||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/commit/6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/commit/6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
||||||
@@ -707,7 +710,7 @@ object ApiSpecModels {
|
|||||||
|"patch":"@@ -1 +1,2 @@\\n-body1\\n\\\\ No newline at end of file\\n+body1\\n+body2\\n\\\\ No newline at end of file"}]
|
|"patch":"@@ -1 +1,2 @@\\n-body1\\n\\\\ No newline at end of file\\n+body1\\n+body2\\n\\\\ No newline at end of file"}]
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonCommitStatus = s"""{
|
val jsonCommitStatus: String = s"""{
|
||||||
|"created_at":"2011-04-14T16:00:49Z",
|
|"created_at":"2011-04-14T16:00:49Z",
|
||||||
|"updated_at":"2011-04-14T16:00:49Z",
|
|"updated_at":"2011-04-14T16:00:49Z",
|
||||||
|"state":"success",
|
|"state":"success",
|
||||||
@@ -719,7 +722,7 @@ object ApiSpecModels {
|
|||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/statuses"
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/statuses"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonCombinedCommitStatus = s"""{
|
val jsonCombinedCommitStatus: String = s"""{
|
||||||
|"state":"success",
|
|"state":"success",
|
||||||
|"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
|"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e",
|
||||||
|"total_count":1,
|
|"total_count":1,
|
||||||
@@ -728,7 +731,7 @@ object ApiSpecModels {
|
|||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/status"
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/status"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonBranchProtectionOutput =
|
val jsonBranchProtectionOutput: String =
|
||||||
"""{
|
"""{
|
||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/branches/main/protection",
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/branches/main/protection",
|
||||||
|"enabled":true,
|
|"enabled":true,
|
||||||
@@ -736,15 +739,25 @@ object ApiSpecModels {
|
|||||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/branches/main/protection/required_status_checks",
|
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/branches/main/protection/required_status_checks",
|
||||||
|"enforcement_level":"everyone",
|
|"enforcement_level":"everyone",
|
||||||
|"contexts":["continuous-integration/travis-ci"],
|
|"contexts":["continuous-integration/travis-ci"],
|
||||||
|"contexts_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/branches/main/protection/required_status_checks/contexts"}
|
|"contexts_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/branches/main/protection/required_status_checks/contexts"
|
||||||
|
|},
|
||||||
|
|"restrictions":{
|
||||||
|
|"users":["admin"]
|
||||||
|
|},
|
||||||
|
|"enforce_admins":{
|
||||||
|
|"enabled":true
|
||||||
|
|}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonBranchProtectionInput =
|
val jsonBranchProtectionInput: String =
|
||||||
"""{
|
"""{
|
||||||
|"enabled":true,
|
|"enabled":true,
|
||||||
|"required_status_checks":{
|
|"required_status_checks":{
|
||||||
|"enforcement_level":"everyone",
|
|"enforcement_level":"everyone",
|
||||||
|"contexts":["continuous-integration/travis-ci"]
|
|"contexts":["continuous-integration/travis-ci"]
|
||||||
|
|},
|
||||||
|
|"restrictions":{
|
||||||
|
|"users":["admin"]
|
||||||
|}
|
|}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
@@ -757,9 +770,9 @@ object ApiSpecModels {
|
|||||||
|"html":"http://gitbucket.exmple.com/octocat/Hello-World/tree/main"}
|
|"html":"http://gitbucket.exmple.com/octocat/Hello-World/tree/main"}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonBranchForList = """{"name":"main","commit":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
|
val jsonBranchForList: String = """{"name":"main","commit":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
|
||||||
|
|
||||||
val jsonContents =
|
val jsonContents: String =
|
||||||
"""{
|
"""{
|
||||||
|"type":"file",
|
|"type":"file",
|
||||||
|"name":"README.md",
|
|"name":"README.md",
|
||||||
@@ -770,14 +783,14 @@ object ApiSpecModels {
|
|||||||
|"download_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/raw/6dcb09b5b57875f334f61aebed695e2e4193db5e/doc/README.md"
|
|"download_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/raw/6dcb09b5b57875f334f61aebed695e2e4193db5e/doc/README.md"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonEndPoint = """{"rate_limit_url":"http://gitbucket.exmple.com/api/v3/rate_limit"}"""
|
val jsonEndPoint: String = """{"rate_limit_url":"http://gitbucket.exmple.com/api/v3/rate_limit"}"""
|
||||||
|
|
||||||
val jsonError = """{
|
val jsonError: String = """{
|
||||||
|"message":"A repository with this name already exists on this account",
|
|"message":"A repository with this name already exists on this account",
|
||||||
|"documentation_url":"https://developer.github.com/v3/repos/#create"
|
|"documentation_url":"https://developer.github.com/v3/repos/#create"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonGroup = """{
|
val jsonGroup: String = """{
|
||||||
|"login":"octocat",
|
|"login":"octocat",
|
||||||
|"description":"Admin group",
|
|"description":"Admin group",
|
||||||
|"created_at":"2011-04-14T16:00:49Z",
|
|"created_at":"2011-04-14T16:00:49Z",
|
||||||
@@ -787,7 +800,7 @@ object ApiSpecModels {
|
|||||||
|"avatar_url":"http://gitbucket.exmple.com/octocat/_avatar"
|
|"avatar_url":"http://gitbucket.exmple.com/octocat/_avatar"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonPlugin = """{
|
val jsonPlugin: String = """{
|
||||||
|"id":"gist",
|
|"id":"gist",
|
||||||
|"name":"Gist Plugin",
|
|"name":"Gist Plugin",
|
||||||
|"version":"4.16.0",
|
|"version":"4.16.0",
|
||||||
@@ -795,12 +808,12 @@ object ApiSpecModels {
|
|||||||
|"jarFileName":"gitbucket-gist-plugin-gitbucket_4.30.0-SNAPSHOT-4.17.0.jar"
|
|"jarFileName":"gitbucket-gist-plugin-gitbucket_4.30.0-SNAPSHOT-4.17.0.jar"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonPusher = """{"name":"octocat","email":"octocat@example.com"}"""
|
val jsonPusher: String = """{"name":"octocat","email":"octocat@example.com"}"""
|
||||||
|
|
||||||
// I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag"
|
// I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag"
|
||||||
val jsonRef = """{"ref":"refs/heads/featureA","object":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
|
val jsonRef: String = """{"ref":"refs/heads/featureA","object":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
|
||||||
|
|
||||||
val jsonRefHeadsMain =
|
val jsonRefHeadsMain: String =
|
||||||
"""{
|
"""{
|
||||||
|"ref": "refs/heads/main",
|
|"ref": "refs/heads/main",
|
||||||
|"node_id": "MDM6UmVmOTM1MDc0NjpyZWZzL2hlYWRzL21hc3Rlcg==",
|
|"node_id": "MDM6UmVmOTM1MDc0NjpyZWZzL2hlYWRzL21hc3Rlcg==",
|
||||||
@@ -812,7 +825,7 @@ object ApiSpecModels {
|
|||||||
|}
|
|}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonRefTag =
|
val jsonRefTag: String =
|
||||||
"""{
|
"""{
|
||||||
|"ref": "refs/tags/1.0",
|
|"ref": "refs/tags/1.0",
|
||||||
|"node_id": "MDM6UmVmOTM1MDc0NjpyZWZzL3RhZ3MvMS4w",
|
|"node_id": "MDM6UmVmOTM1MDc0NjpyZWZzL3RhZ3MvMS4w",
|
||||||
@@ -824,7 +837,7 @@ object ApiSpecModels {
|
|||||||
|}
|
|}
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonReleaseAsset =
|
val jsonReleaseAsset: String =
|
||||||
s"""{
|
s"""{
|
||||||
|"name":"release.zip",
|
|"name":"release.zip",
|
||||||
|"size":100,
|
|"size":100,
|
||||||
@@ -833,7 +846,7 @@ object ApiSpecModels {
|
|||||||
|"browser_download_url":"http://gitbucket.exmple.com/octocat/Hello-World/releases/tag1/assets/${assetFileName}"
|
|"browser_download_url":"http://gitbucket.exmple.com/octocat/Hello-World/releases/tag1/assets/${assetFileName}"
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
|
|
||||||
val jsonRelease =
|
val jsonRelease: String =
|
||||||
s"""{
|
s"""{
|
||||||
|"name":"release1",
|
|"name":"release1",
|
||||||
|"tag_name":"tag1",
|
|"tag_name":"tag1",
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package gitbucket.core.api
|
package gitbucket.core.api
|
||||||
|
|
||||||
import org.json4s.Formats
|
import org.json4s.{Formats, JValue, jvalue2extractable}
|
||||||
import org.json4s.jackson.JsonMethods
|
import org.json4s.jackson.JsonMethods
|
||||||
import org.json4s.jvalue2extractable
|
import org.scalatest.Assertion
|
||||||
import org.scalatest.funsuite.AnyFunSuite
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
|
||||||
class JsonFormatSpec extends AnyFunSuite {
|
class JsonFormatSpec extends AnyFunSuite {
|
||||||
import ApiSpecModels._
|
import ApiSpecModels.*
|
||||||
implicit val format: Formats = JsonFormat.jsonFormats
|
implicit val format: Formats = JsonFormat.jsonFormats
|
||||||
|
|
||||||
private def expected(json: String) = json.replaceAll("\n", "")
|
private def expected(json: String) = json.replaceAll("\n", "")
|
||||||
def normalizeJson(json: String) = {
|
def normalizeJson(json: String): JValue = {
|
||||||
org.json4s.jackson.parseJson(json)
|
org.json4s.jackson.parseJson(json)
|
||||||
}
|
}
|
||||||
def assertEqualJson(actual: String, expected: String) = {
|
def assertEqualJson(actual: String, expected: String): Assertion = {
|
||||||
assert(normalizeJson(actual) == normalizeJson(expected))
|
assert(normalizeJson(actual) == normalizeJson(expected))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,9 @@ class JsonFormatSpec extends AnyFunSuite {
|
|||||||
assert(JsonFormat(apiBranchProtectionOutput) == expected(jsonBranchProtectionOutput))
|
assert(JsonFormat(apiBranchProtectionOutput) == expected(jsonBranchProtectionOutput))
|
||||||
}
|
}
|
||||||
test("deserialize apiBranchProtection") {
|
test("deserialize apiBranchProtection") {
|
||||||
assert(JsonMethods.parse(jsonBranchProtectionInput).extract[ApiBranchProtection] == apiBranchProtectionInput)
|
assert(
|
||||||
|
JsonMethods.parse(jsonBranchProtectionInput).extract[ApiBranchProtectionResponse] == apiBranchProtectionInput
|
||||||
|
)
|
||||||
}
|
}
|
||||||
test("apiBranch") {
|
test("apiBranch") {
|
||||||
assert(JsonFormat(apiBranch) == expected(jsonBranch))
|
assert(JsonFormat(apiBranch) == expected(jsonBranch))
|
||||||
|
|||||||
@@ -29,26 +29,28 @@ class ProtectedBranchServiceSpec
|
|||||||
it("should enable and update and disable") {
|
it("should enable and update and disable") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
generateNewUserWithDBRepository("user1", "repo1")
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Nil)
|
enableBranchProtection("user1", "repo1", "branch", false, false, Nil, false, Nil)
|
||||||
assert(
|
assert(
|
||||||
getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo(
|
getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo(
|
||||||
"user1",
|
"user1",
|
||||||
"repo1",
|
"repo1",
|
||||||
"branch",
|
"branch",
|
||||||
true,
|
true,
|
||||||
Nil,
|
None,
|
||||||
false
|
false,
|
||||||
|
None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
enableBranchProtection("user1", "repo1", "branch", true, Seq("hoge", "huge"))
|
enableBranchProtection("user1", "repo1", "branch", true, true, Seq("hoge", "huge"), false, Nil)
|
||||||
assert(
|
assert(
|
||||||
getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo(
|
getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo(
|
||||||
"user1",
|
"user1",
|
||||||
"repo1",
|
"repo1",
|
||||||
"branch",
|
"branch",
|
||||||
true,
|
enabled = true,
|
||||||
Seq("hoge", "huge"),
|
contexts = Some(Seq("hoge", "huge")),
|
||||||
true
|
enforceAdmins = true,
|
||||||
|
restrictionsUsers = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
disableBranchProtection("user1", "repo1", "branch")
|
disableBranchProtection("user1", "repo1", "branch")
|
||||||
@@ -57,21 +59,21 @@ class ProtectedBranchServiceSpec
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it("should empty contexts is no-include-administrators") {
|
it("should empty contexts is include-administrators") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
generateNewUserWithDBRepository("user1", "repo1")
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Nil)
|
enableBranchProtection("user1", "repo1", "branch", false, false, Nil, false, Nil)
|
||||||
assert(getProtectedBranchInfo("user1", "repo1", "branch").includeAdministrators == false)
|
assert(!getProtectedBranchInfo("user1", "repo1", "branch").enforceAdmins)
|
||||||
enableBranchProtection("user1", "repo1", "branch", true, Nil)
|
enableBranchProtection("user1", "repo1", "branch", true, false, Nil, false, Nil)
|
||||||
assert(getProtectedBranchInfo("user1", "repo1", "branch").includeAdministrators == false)
|
assert(getProtectedBranchInfo("user1", "repo1", "branch").enforceAdmins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it("getProtectedBranchList") {
|
it("getProtectedBranchList") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
generateNewUserWithDBRepository("user1", "repo1")
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Nil)
|
enableBranchProtection("user1", "repo1", "branch", false, false, Nil, false, Nil)
|
||||||
enableBranchProtection("user1", "repo1", "branch2", false, Seq("fuga"))
|
enableBranchProtection("user1", "repo1", "branch2", false, false, Seq("fuga"), false, Nil)
|
||||||
enableBranchProtection("user1", "repo1", "branch3", true, Seq("hoge"))
|
enableBranchProtection("user1", "repo1", "branch3", true, false, Seq("hoge"), false, Nil)
|
||||||
assert(getProtectedBranchList("user1", "repo1").toSet == Set("branch", "branch2", "branch3"))
|
assert(getProtectedBranchList("user1", "repo1").toSet == Set("branch", "branch2", "branch3"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,12 +89,12 @@ class ProtectedBranchServiceSpec
|
|||||||
ReceiveCommand.Type.UPDATE_NONFASTFORWARD
|
ReceiveCommand.Type.UPDATE_NONFASTFORWARD
|
||||||
)
|
)
|
||||||
generateNewUserWithDBRepository("user1", "repo1")
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false).isEmpty)
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Nil)
|
enableBranchProtection("user1", "repo1", "branch", false, false, Nil, false, Nil)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == Some(
|
receiveHook
|
||||||
"Cannot force-push to a protected branch"
|
.preReceive("user1", "repo1", rp, rc, "user1", false)
|
||||||
)
|
.contains("Cannot force-push to a protected branch")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,12 +111,12 @@ class ProtectedBranchServiceSpec
|
|||||||
ReceiveCommand.Type.UPDATE_NONFASTFORWARD
|
ReceiveCommand.Type.UPDATE_NONFASTFORWARD
|
||||||
)
|
)
|
||||||
generateNewUserWithDBRepository("user1", "repo1")
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false).isEmpty)
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Nil)
|
enableBranchProtection("user1", "repo1", "branch", false, false, Nil, false, Nil)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == Some(
|
receiveHook
|
||||||
"Cannot force-push to a protected branch"
|
.preReceive("user1", "repo1", rp, rc, "user2", false)
|
||||||
)
|
.contains("Cannot force-push to a protected branch")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,33 +133,33 @@ class ProtectedBranchServiceSpec
|
|||||||
ReceiveCommand.Type.UPDATE
|
ReceiveCommand.Type.UPDATE
|
||||||
)
|
)
|
||||||
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false).isEmpty)
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Seq("must"))
|
enableBranchProtection("user1", "repo1", "branch", false, true, Seq("must"), false, Nil)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == Some(
|
receiveHook
|
||||||
"Required status check \"must\" is expected"
|
.preReceive("user1", "repo1", rp, rc, "user2", false)
|
||||||
)
|
.contains("Required status check \"must\" is expected")
|
||||||
)
|
)
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Seq("must", "must2"))
|
enableBranchProtection("user1", "repo1", "branch", false, true, Seq("must", "must2"), false, Nil)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == Some(
|
receiveHook
|
||||||
"2 of 2 required status checks are expected"
|
.preReceive("user1", "repo1", rp, rc, "user2", false)
|
||||||
)
|
.contains("2 of 2 required status checks are expected")
|
||||||
)
|
)
|
||||||
createCommitStatus("user1", "repo1", sha2, "context", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha2, "context", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == Some(
|
receiveHook
|
||||||
"2 of 2 required status checks are expected"
|
.preReceive("user1", "repo1", rp, rc, "user2", false)
|
||||||
)
|
.contains("2 of 2 required status checks are expected")
|
||||||
)
|
)
|
||||||
createCommitStatus("user1", "repo1", sha2, "must", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha2, "must", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == Some(
|
receiveHook
|
||||||
"Required status check \"must2\" is expected"
|
.preReceive("user1", "repo1", rp, rc, "user2", false)
|
||||||
)
|
.contains("Required status check \"must2\" is expected")
|
||||||
)
|
)
|
||||||
createCommitStatus("user1", "repo1", sha2, "must2", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha2, "must2", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false).isEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,37 +175,60 @@ class ProtectedBranchServiceSpec
|
|||||||
ReceiveCommand.Type.UPDATE
|
ReceiveCommand.Type.UPDATE
|
||||||
)
|
)
|
||||||
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false).isEmpty)
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Seq("must"))
|
enableBranchProtection("user1", "repo1", "branch", false, true, Seq("must"), false, Nil)
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false).isEmpty)
|
||||||
enableBranchProtection("user1", "repo1", "branch", true, Seq("must"))
|
enableBranchProtection("user1", "repo1", "branch", true, true, Seq("must"), false, Nil)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == Some(
|
receiveHook
|
||||||
"Required status check \"must\" is expected"
|
.preReceive("user1", "repo1", rp, rc, "user1", false)
|
||||||
)
|
.contains("Required status check \"must\" is expected")
|
||||||
)
|
)
|
||||||
enableBranchProtection("user1", "repo1", "branch", false, Seq("must", "must2"))
|
enableBranchProtection("user1", "repo1", "branch", false, true, Seq("must", "must2"), false, Nil)
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false).isEmpty)
|
||||||
enableBranchProtection("user1", "repo1", "branch", true, Seq("must", "must2"))
|
enableBranchProtection("user1", "repo1", "branch", true, true, Seq("must", "must2"), false, Nil)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == Some(
|
receiveHook
|
||||||
"2 of 2 required status checks are expected"
|
.preReceive("user1", "repo1", rp, rc, "user1", false)
|
||||||
)
|
.contains("2 of 2 required status checks are expected")
|
||||||
)
|
)
|
||||||
createCommitStatus("user1", "repo1", sha2, "context", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha2, "context", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == Some(
|
receiveHook
|
||||||
"2 of 2 required status checks are expected"
|
.preReceive("user1", "repo1", rp, rc, "user1", false)
|
||||||
)
|
.contains("2 of 2 required status checks are expected")
|
||||||
)
|
)
|
||||||
createCommitStatus("user1", "repo1", sha2, "must", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha2, "must", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(
|
assert(
|
||||||
receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == Some(
|
receiveHook
|
||||||
"Required status check \"must2\" is expected"
|
.preReceive("user1", "repo1", rp, rc, "user1", false)
|
||||||
)
|
.contains("Required status check \"must2\" is expected")
|
||||||
)
|
)
|
||||||
createCommitStatus("user1", "repo1", sha2, "must2", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha2, "must2", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false) == None)
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1", false).isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it("should restrict push to allowed users only") {
|
||||||
|
withTestDB { implicit session =>
|
||||||
|
withTestRepository { git =>
|
||||||
|
val rp = new ReceivePack(git.getRepository)
|
||||||
|
rp.setAllowNonFastForwards(true)
|
||||||
|
val rc = new ReceiveCommand(
|
||||||
|
ObjectId.fromString(sha),
|
||||||
|
ObjectId.fromString(sha2),
|
||||||
|
"refs/heads/branch",
|
||||||
|
ReceiveCommand.Type.UPDATE
|
||||||
|
)
|
||||||
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
|
generateNewAccount("user2")
|
||||||
|
enableBranchProtection("user1", "repo1", "branch", false, false, Nil, true, Seq("user2"))
|
||||||
|
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2", false).isEmpty)
|
||||||
|
assert(
|
||||||
|
receiveHook
|
||||||
|
.preReceive("user1", "repo1", rp, rc, "user3", false)
|
||||||
|
.contains("You do not have permission to push to this branch")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,29 +237,53 @@ class ProtectedBranchServiceSpec
|
|||||||
it("administrator is owner") {
|
it("administrator is owner") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
generateNewUserWithDBRepository("user1", "repo1")
|
generateNewUserWithDBRepository("user1", "repo1")
|
||||||
val x = ProtectedBranchInfo("user1", "repo1", "branch", true, Nil, false)
|
val x = ProtectedBranchInfo(
|
||||||
assert(x.isAdministrator("user1") == true)
|
"user1",
|
||||||
assert(x.isAdministrator("user2") == false)
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Nil),
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
)
|
||||||
|
assert(x.isAdministrator("user1"))
|
||||||
|
assert(!x.isAdministrator("user2"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it("administrator is manager") {
|
it("administrator is manager") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
val x = ProtectedBranchInfo("grp1", "repo1", "branch", true, Nil, false)
|
val x = ProtectedBranchInfo(
|
||||||
|
"grp1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Nil),
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
)
|
||||||
x.createGroup("grp1", None, None)
|
x.createGroup("grp1", None, None)
|
||||||
generateNewAccount("user1")
|
generateNewAccount("user1")
|
||||||
generateNewAccount("user2")
|
generateNewAccount("user2")
|
||||||
generateNewAccount("user3")
|
generateNewAccount("user3")
|
||||||
|
|
||||||
x.updateGroupMembers("grp1", List("user1" -> true, "user2" -> false))
|
x.updateGroupMembers("grp1", List("user1" -> true, "user2" -> false))
|
||||||
assert(x.isAdministrator("user1") == true)
|
assert(x.isAdministrator("user1"))
|
||||||
assert(x.isAdministrator("user2") == false)
|
assert(!x.isAdministrator("user2"))
|
||||||
assert(x.isAdministrator("user3") == false)
|
assert(!x.isAdministrator("user3"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it("unSuccessedContexts") {
|
it("unSuccessedContexts") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
||||||
val x = ProtectedBranchInfo("user1", "repo1", "branch", true, List("must"), false)
|
val x = ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(List("must")),
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
)
|
||||||
assert(x.unSuccessedContexts(sha) == Set("must"))
|
assert(x.unSuccessedContexts(sha) == Set("must"))
|
||||||
createCommitStatus("user1", "repo1", sha, "context", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha, "context", CommitState.SUCCESS, None, None, now, user1)
|
||||||
assert(x.unSuccessedContexts(sha) == Set("must"))
|
assert(x.unSuccessedContexts(sha) == Set("must"))
|
||||||
@@ -251,7 +300,15 @@ class ProtectedBranchServiceSpec
|
|||||||
it("unSuccessedContexts when empty") {
|
it("unSuccessedContexts when empty") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
val user1 = generateNewUserWithDBRepository("user1", "repo1")
|
||||||
val x = ProtectedBranchInfo("user1", "repo1", "branch", true, Nil, false)
|
val x = ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Nil),
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
)
|
||||||
val sha = "0c77148632618b59b6f70004e3084002be2b8804"
|
val sha = "0c77148632618b59b6f70004e3084002be2b8804"
|
||||||
assert(x.unSuccessedContexts(sha) == Set())
|
assert(x.unSuccessedContexts(sha) == Set())
|
||||||
createCommitStatus("user1", "repo1", sha, "context", CommitState.SUCCESS, None, None, now, user1)
|
createCommitStatus("user1", "repo1", sha, "context", CommitState.SUCCESS, None, None, now, user1)
|
||||||
@@ -261,23 +318,63 @@ class ProtectedBranchServiceSpec
|
|||||||
it("if disabled, needStatusCheck is false") {
|
it("if disabled, needStatusCheck is false") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
assert(
|
assert(
|
||||||
ProtectedBranchInfo("user1", "repo1", "branch", false, Seq("must"), true).needStatusCheck("user1") == false
|
!ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = false,
|
||||||
|
contexts = Some(Seq("must")),
|
||||||
|
enforceAdmins = true,
|
||||||
|
restrictionsUsers = None
|
||||||
|
).needStatusCheck("user1")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it("needStatusCheck includeAdministrators") {
|
it("needStatusCheck includeAdministrators") {
|
||||||
withTestDB { implicit session =>
|
withTestDB { implicit session =>
|
||||||
assert(
|
assert(
|
||||||
ProtectedBranchInfo("user1", "repo1", "branch", true, Seq("must"), false).needStatusCheck("user2") == true
|
ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Seq("must")),
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
).needStatusCheck("user2")
|
||||||
)
|
)
|
||||||
assert(
|
assert(
|
||||||
ProtectedBranchInfo("user1", "repo1", "branch", true, Seq("must"), false).needStatusCheck("user1") == false
|
!ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Seq("must")),
|
||||||
|
enforceAdmins = false,
|
||||||
|
restrictionsUsers = None
|
||||||
|
).needStatusCheck("user1")
|
||||||
)
|
)
|
||||||
assert(
|
assert(
|
||||||
ProtectedBranchInfo("user1", "repo1", "branch", true, Seq("must"), true).needStatusCheck("user2") == true
|
ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Seq("must")),
|
||||||
|
enforceAdmins = true,
|
||||||
|
restrictionsUsers = None
|
||||||
|
).needStatusCheck("user2")
|
||||||
)
|
)
|
||||||
assert(
|
assert(
|
||||||
ProtectedBranchInfo("user1", "repo1", "branch", true, Seq("must"), true).needStatusCheck("user1") == true
|
ProtectedBranchInfo(
|
||||||
|
"user1",
|
||||||
|
"repo1",
|
||||||
|
"branch",
|
||||||
|
enabled = true,
|
||||||
|
contexts = Some(Seq("must")),
|
||||||
|
enforceAdmins = true,
|
||||||
|
restrictionsUsers = None
|
||||||
|
).needStatusCheck("user1")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class RepositoryServiceSpec extends AnyFunSuite with ServiceSpecBase with Reposi
|
|||||||
now = new java.util.Date
|
now = new java.util.Date
|
||||||
)
|
)
|
||||||
|
|
||||||
service.enableBranchProtection("root", "repo", "branch", true, Seq("must1", "must2"))
|
service.enableBranchProtection("root", "repo", "branch", true, true, Seq("must1", "must2"), false, Nil)
|
||||||
|
|
||||||
val orgPbi = service.getProtectedBranchInfo("root", "repo", "branch")
|
val orgPbi = service.getProtectedBranchInfo("root", "repo", "branch")
|
||||||
val org = service.getCommitStatus("root", "repo", id).get
|
val org = service.getCommitStatus("root", "repo", id).get
|
||||||
|
|||||||
Reference in New Issue
Block a user