diff --git a/src/main/scala/app/IssuesController.scala b/src/main/scala/app/IssuesController.scala index b63625d..23d40c6 100644 --- a/src/main/scala/app/IssuesController.scala +++ b/src/main/scala/app/IssuesController.scala @@ -4,16 +4,16 @@ import service._ import IssuesService._ -import util.UsersOnlyAuthenticator +import util.{CollaboratorsAuthenticator, ReferrerAuthenticator, ReadableUsersAuthenticator} import org.scalatra.Ok class IssuesController extends IssuesControllerBase with IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService - with UsersOnlyAuthenticator + with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator trait IssuesControllerBase extends ControllerBase { self: IssuesService with RepositoryService with LabelsService with MilestonesService - with UsersOnlyAuthenticator => + with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator => case class IssueCreateForm(title: String, content: Option[String], assignedUserName: Option[String], milestoneId: Option[Int], labelNames: Option[String]) @@ -40,19 +40,19 @@ "content" -> trim(label("Comment", text(required))) )(CommentForm.apply) - get("/:owner/:repository/issues"){ + get("/:owner/:repository/issues")(referrersOnly { searchIssues("all") - } + }) - get("/:owner/:repository/issues/assigned/:userName"){ + get("/:owner/:repository/issues/assigned/:userName")(referrersOnly { searchIssues("assigned") - } + }) - get("/:owner/:repository/issues/created_by/:userName"){ + get("/:owner/:repository/issues/created_by/:userName")(referrersOnly { searchIssues("created_by") - } + }) - get("/:owner/:repository/issues/:id"){ + get("/:owner/:repository/issues/:id")(referrersOnly { val owner = params("owner") val repository = params("repository") val issueId = params("id") @@ -67,10 +67,9 @@ getLabels(owner, repository), getRepository(owner, repository, baseUrl).get) } getOrElse NotFound - } + }) - // TODO requires users only and readable repository checking - get("/:owner/:repository/issues/new")( usersOnly { + get("/:owner/:repository/issues/new")( readableUsersOnly { val owner = params("owner") val repository = params("repository") @@ -83,8 +82,7 @@ } getOrElse NotFound }) - // TODO requires users only and readable repository checking - post("/:owner/:repository/issues/new", issueCreateForm)( usersOnly { form => + post("/:owner/:repository/issues/new", issueCreateForm)( readableUsersOnly { form => val owner = params("owner") val repository = params("repository") @@ -116,7 +114,7 @@ } // TODO requires users only and readable repository checking - post("/:owner/:repository/issue_comments/new", commentForm)( usersOnly { form => + post("/:owner/:repository/issue_comments/new", commentForm)( referrersOnly { form => val owner = params("owner") val repository = params("repository") val action = params.get("action") filter { action => @@ -168,8 +166,7 @@ } getOrElse NotFound } - // TODO Authenticator - ajaxPost("/:owner/:repository/issues/:id/label/new"){ + ajaxPost("/:owner/:repository/issues/:id/label/new")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val issueId = params("id").toInt @@ -177,10 +174,9 @@ registerIssueLabel(owner, repository, issueId, params("labelId").toInt) issues.html.labellist(getIssueLabels(owner, repository, issueId)) - } + }) - // TODO Authenticator - ajaxPost("/:owner/:repository/issues/:id/label/delete"){ + ajaxPost("/:owner/:repository/issues/:id/label/delete")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val issueId = params("id").toInt @@ -188,9 +184,9 @@ deleteIssueLabel(owner, repository, issueId, params("labelId").toInt) issues.html.labellist(getIssueLabels(owner, repository, issueId)) - } + }) - ajaxPost("/:owner/:repository/issues/assign/:id"){ + ajaxPost("/:owner/:repository/issues/:id/assign")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val issueId = params("id").toInt @@ -201,9 +197,9 @@ case Some(userName) => updateAssignedUserName(owner, repository, issueId, Some(userName)) } Ok("updated") - } + }) - ajaxPost("/:owner/:repository/issues/milestone/:id"){ + ajaxPost("/:owner/:repository/issues/:id/milestone")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val issueId = params("id").toInt @@ -214,7 +210,7 @@ case Some(milestoneId) => updateMilestoneId(owner, repository, issueId, Some(milestoneId.toInt)) } Ok("updated") - } + }) private def searchIssues(filter: String) = { val owner = params("owner") diff --git a/src/main/scala/app/LabelsController.scala b/src/main/scala/app/LabelsController.scala index dbf43e5..ee2e73c 100644 --- a/src/main/scala/app/LabelsController.scala +++ b/src/main/scala/app/LabelsController.scala @@ -2,13 +2,13 @@ import jp.sf.amateras.scalatra.forms._ import service._ -import util.WritableRepositoryAuthenticator +import util.CollaboratorsAuthenticator class LabelsController extends LabelsControllerBase - with LabelsService with RepositoryService with AccountService with WritableRepositoryAuthenticator + with LabelsService with RepositoryService with AccountService with CollaboratorsAuthenticator trait LabelsControllerBase extends ControllerBase { - self: LabelsService with RepositoryService with WritableRepositoryAuthenticator => + self: LabelsService with RepositoryService with CollaboratorsAuthenticator => case class LabelForm(labelName: String, color: String) @@ -22,7 +22,7 @@ "editColor" -> trim(label("Color", text(required, color))) )(LabelForm.apply) - post("/:owner/:repository/issues/label/new", newForm)(writableRepository { form => + post("/:owner/:repository/issues/label/new", newForm)(collaboratorsOnly { form => val owner = params("owner") val repository = params("repository") @@ -31,7 +31,7 @@ redirect("/%s/%s/issues".format(owner, repository)) }) - ajaxGet("/:owner/:repository/issues/label/edit")(writableRepository { + ajaxGet("/:owner/:repository/issues/label/edit")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") @@ -39,7 +39,7 @@ .map(issues.labels.html.editlist(getLabels(owner, repository), _)) getOrElse NotFound() }) - ajaxGet("/:owner/:repository/issues/label/:labelId/edit")(writableRepository { + ajaxGet("/:owner/:repository/issues/label/:labelId/edit")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val labelId = params("labelId").toInt @@ -49,7 +49,7 @@ } getOrElse NotFound() }) - ajaxPost("/:owner/:repository/issues/label/:labelId/edit", editForm)(writableRepository { form => + ajaxPost("/:owner/:repository/issues/label/:labelId/edit", editForm)(collaboratorsOnly { form => val owner = params("owner") val repository = params("repository") val labelId = params("labelId").toInt @@ -60,7 +60,7 @@ } getOrElse NotFound() }) - ajaxGet("/:owner/:repository/issues/label/:labelId/delete")(writableRepository { + ajaxGet("/:owner/:repository/issues/label/:labelId/delete")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val labelId = params("labelId").toInt diff --git a/src/main/scala/app/MilestonesController.scala b/src/main/scala/app/MilestonesController.scala index 08d3b06..7aca76d 100644 --- a/src/main/scala/app/MilestonesController.scala +++ b/src/main/scala/app/MilestonesController.scala @@ -3,15 +3,15 @@ import jp.sf.amateras.scalatra.forms._ import service._ -import util.{WritableRepositoryAuthenticator, ReadableRepositoryAuthenticator, UsersOnlyAuthenticator} +import util.{CollaboratorsAuthenticator, ReferrerAuthenticator, UsersOnlyAuthenticator} class MilestonesController extends MilestonesControllerBase with MilestonesService with RepositoryService with AccountService - with ReadableRepositoryAuthenticator with WritableRepositoryAuthenticator + with ReferrerAuthenticator with CollaboratorsAuthenticator trait MilestonesControllerBase extends ControllerBase { self: MilestonesService with RepositoryService - with ReadableRepositoryAuthenticator with WritableRepositoryAuthenticator => + with ReferrerAuthenticator with CollaboratorsAuthenticator => case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date]) @@ -21,7 +21,7 @@ "dueDate" -> trim(label("Due Date", optional(date()))) )(MilestoneForm.apply) - get("/:owner/:repository/issues/milestones")(readableRepository { + get("/:owner/:repository/issues/milestones")(referrersOnly { val owner = params("owner") val repository = params("repository") val state = params.getOrElse("state", "open") @@ -34,14 +34,14 @@ } getOrElse NotFound }) - get("/:owner/:repository/issues/milestones/new")(writableRepository { + get("/:owner/:repository/issues/milestones/new")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") getRepository(owner, repository, baseUrl).map(issues.milestones.html.edit(None, _)) getOrElse NotFound }) - post("/:owner/:repository/issues/milestones/new", milestoneForm)(writableRepository { form => + post("/:owner/:repository/issues/milestones/new", milestoneForm)(collaboratorsOnly { form => val owner = params("owner") val repository = params("repository") @@ -49,7 +49,7 @@ redirect("/%s/%s/issues/milestones".format(owner, repository)) }) - get("/:owner/:repository/issues/milestones/:milestoneId/edit")(writableRepository { + get("/:owner/:repository/issues/milestones/:milestoneId/edit")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val milestoneId = params("milestoneId").toInt @@ -58,7 +58,7 @@ issues.milestones.html.edit(getMilestone(owner, repository, milestoneId), _)) getOrElse NotFound }) - post("/:owner/:repository/issues/milestones/:milestoneId/edit", milestoneForm)(writableRepository { form => + post("/:owner/:repository/issues/milestones/:milestoneId/edit", milestoneForm)(collaboratorsOnly { form => val owner = params("owner") val repository = params("repository") val milestoneId = params("milestoneId").toInt @@ -69,7 +69,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/issues/milestones/:milestoneId/close")(writableRepository { + get("/:owner/:repository/issues/milestones/:milestoneId/close")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val milestoneId = params("milestoneId").toInt @@ -80,7 +80,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/issues/milestones/:milestoneId/open")(writableRepository { + get("/:owner/:repository/issues/milestones/:milestoneId/open")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val milestoneId = params("milestoneId").toInt @@ -91,7 +91,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/issues/milestones/:milestoneId/delete")(writableRepository { + get("/:owner/:repository/issues/milestones/:milestoneId/delete")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val milestoneId = params("milestoneId").toInt diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index 5da0170..a7a85db 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -2,7 +2,7 @@ import util.Directory._ import util.Implicits._ -import _root_.util.{ReadableRepositoryAuthenticator, JGitUtil, FileUtil} +import _root_.util.{ReferrerAuthenticator, JGitUtil, FileUtil} import service._ import org.scalatra._ import java.io.File @@ -12,18 +12,18 @@ import org.eclipse.jgit.treewalk._ class RepositoryViewerController extends RepositoryViewerControllerBase - with RepositoryService with AccountService with ReadableRepositoryAuthenticator + with RepositoryService with AccountService with ReferrerAuthenticator /** * The repository viewer. */ trait RepositoryViewerControllerBase extends ControllerBase { - self: RepositoryService with AccountService with ReadableRepositoryAuthenticator => + self: RepositoryService with AccountService with ReferrerAuthenticator => /** * Returns converted HTML from Markdown for preview. */ - post("/:owner/:repository/_preview")(readableRepository { + post("/:owner/:repository/_preview")(referrersOnly { val owner = params("owner") val repository = params("repository") val content = params("content") @@ -39,7 +39,7 @@ /** * Displays the file list of the repository root and the default branch. */ - get("/:owner/:repository")(readableRepository { + get("/:owner/:repository")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -49,7 +49,7 @@ /** * Displays the file list of the repository root and the specified branch. */ - get("/:owner/:repository/tree/:id")(readableRepository { + get("/:owner/:repository/tree/:id")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -59,7 +59,7 @@ /** * Displays the file list of the specified path and branch. */ - get("/:owner/:repository/tree/:id/*")(readableRepository { + get("/:owner/:repository/tree/:id/*")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -69,7 +69,7 @@ /** * Displays the commit list of the specified branch. */ - get("/:owner/:repository/commits/:branch")(readableRepository { + get("/:owner/:repository/commits/:branch")(referrersOnly { val owner = params("owner") val repository = params("repository") val branchName = params("branch") @@ -89,7 +89,7 @@ /** * Displays the commit list of the specified resource. */ - get("/:owner/:repository/commits/:branch/*")(readableRepository { + get("/:owner/:repository/commits/:branch/*")(referrersOnly { val owner = params("owner") val repository = params("repository") val branchName = params("branch") @@ -111,7 +111,7 @@ /** * Displays the file content of the specified branch or commit. */ - get("/:owner/:repository/blob/:id/*")(readableRepository { + get("/:owner/:repository/blob/:id/*")(referrersOnly { val owner = params("owner") val repository = params("repository") val id = params("id") // branch name or commit id @@ -169,7 +169,7 @@ /** * Displays details of the specified commit. */ - get("/:owner/:repository/commit/:id")(readableRepository { + get("/:owner/:repository/commit/:id")(referrersOnly { val owner = params("owner") val repository = params("repository") val id = params("id") @@ -188,7 +188,7 @@ /** * Displays tags. */ - get("/:owner/:repository/tags")(readableRepository { + get("/:owner/:repository/tags")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -198,7 +198,7 @@ /** * Download repository contents as an archive. */ - get("/:owner/:repository/archive/:name")(readableRepository { + get("/:owner/:repository/archive/:name")(referrersOnly { val owner = params("owner") val repository = params("repository") val name = params("name") diff --git a/src/main/scala/app/WikiController.scala b/src/main/scala/app/WikiController.scala index 6fe73af..efebbac 100644 --- a/src/main/scala/app/WikiController.scala +++ b/src/main/scala/app/WikiController.scala @@ -1,15 +1,15 @@ package app import service._ -import util.{WritableRepositoryAuthenticator, ReadableRepositoryAuthenticator, JGitUtil} +import util.{CollaboratorsAuthenticator, ReferrerAuthenticator, JGitUtil} import util.Directory._ import jp.sf.amateras.scalatra.forms._ class WikiController extends WikiControllerBase - with WikiService with RepositoryService with AccountService with WritableRepositoryAuthenticator with ReadableRepositoryAuthenticator + with WikiService with RepositoryService with AccountService with CollaboratorsAuthenticator with ReferrerAuthenticator trait WikiControllerBase extends ControllerBase { - self: WikiService with RepositoryService with WritableRepositoryAuthenticator with ReadableRepositoryAuthenticator => + self: WikiService with RepositoryService with CollaboratorsAuthenticator with ReferrerAuthenticator => case class WikiPageEditForm(pageName: String, content: String, message: Option[String], currentPageName: String) @@ -27,7 +27,7 @@ "currentPageName" -> trim(label("Current page name" , text(required))) )(WikiPageEditForm.apply) - get("/:owner/:repository/wiki")(readableRepository { + get("/:owner/:repository/wiki")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -38,7 +38,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/:page")(readableRepository { + get("/:owner/:repository/wiki/:page")(referrersOnly { val owner = params("owner") val repository = params("repository") val pageName = params("page") @@ -50,7 +50,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/:page/_history")(readableRepository { + get("/:owner/:repository/wiki/:page/_history")(referrersOnly { val owner = params("owner") val repository = params("repository") val page = params("page") @@ -62,7 +62,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/:page/_compare/:commitId")(readableRepository { + get("/:owner/:repository/wiki/:page/_compare/:commitId")(referrersOnly { val owner = params("owner") val repository = params("repository") val page = params("page") @@ -75,7 +75,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/_compare/:commitId")(readableRepository { + get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { val owner = params("owner") val repository = params("repository") val commitId = params("commitId").split("\\.\\.\\.") @@ -87,7 +87,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/:page/_edit")(writableRepository { + get("/:owner/:repository/wiki/:page/_edit")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val page = params("page") @@ -96,7 +96,7 @@ wiki.html.edit(page, getWikiPage(owner, repository, page), _)) getOrElse NotFound }) - post("/:owner/:repository/wiki/_edit", editForm)(writableRepository { form => + post("/:owner/:repository/wiki/_edit", editForm)(collaboratorsOnly { form => val owner = params("owner") val repository = params("repository") @@ -107,14 +107,14 @@ redirect("%s/%s/wiki/%s".format(owner, repository, form.pageName)) }) - get("/:owner/:repository/wiki/_new")(writableRepository { + get("/:owner/:repository/wiki/_new")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") getRepository(owner, repository, baseUrl).map(wiki.html.edit("", None, _)) getOrElse NotFound }) - post("/:owner/:repository/wiki/_new", newForm)(writableRepository { form => + post("/:owner/:repository/wiki/_new", newForm)(collaboratorsOnly { form => val owner = params("owner") val repository = params("repository") @@ -124,7 +124,7 @@ redirect("%s/%s/wiki/%s".format(owner, repository, form.pageName)) }) - get("/:owner/:repository/wiki/:page/_delete")(writableRepository { + get("/:owner/:repository/wiki/:page/_delete")(collaboratorsOnly { val owner = params("owner") val repository = params("repository") val page = params("page") @@ -135,7 +135,7 @@ redirect("%s/%s/wiki".format(owner, repository)) }) - get("/:owner/:repository/wiki/_pages")(readableRepository { + get("/:owner/:repository/wiki/_pages")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -144,7 +144,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/_history")(readableRepository { + get("/:owner/:repository/wiki/_history")(referrersOnly { val owner = params("owner") val repository = params("repository") @@ -155,7 +155,7 @@ } getOrElse NotFound }) - get("/:owner/:repository/wiki/_blob/*")(readableRepository { + get("/:owner/:repository/wiki/_blob/*")(referrersOnly { val owner = params("owner") val repository = params("repository") val path = multiParams("splat").head diff --git a/src/main/scala/util/Authenticator.scala b/src/main/scala/util/Authenticator.scala index 7513864..6395a48 100644 --- a/src/main/scala/util/Authenticator.scala +++ b/src/main/scala/util/Authenticator.scala @@ -60,9 +60,9 @@ /** * Allows only collaborators and administrators. */ -trait WritableRepositoryAuthenticator { self: ControllerBase with RepositoryService => - protected def writableRepository(action: => Any) = { authenticate(action) } - protected def writableRepository[T](action: T => Any) = (form: T) => authenticate({action(form)}) +trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService => + protected def collaboratorsOnly(action: => Any) = { authenticate(action) } + protected def collaboratorsOnly[T](action: T => Any) = (form: T) => authenticate({action(form)}) private def authenticate(action: => Any) = { val paths = request.getRequestURI.split("/") @@ -78,9 +78,9 @@ /** * Allows only the repository owner and administrators. */ -trait ReadableRepositoryAuthenticator { self: ControllerBase with RepositoryService => - protected def readableRepository(action: => Any) = { authenticate(action) } - protected def readableRepository[T](action: T => Any) = (form: T) => authenticate({action(form)}) +trait ReferrerAuthenticator { self: ControllerBase with RepositoryService => + protected def referrersOnly(action: => Any) = { authenticate(action) } + protected def referrersOnly[T](action: T => Any) = (form: T) => authenticate({action(form)}) private def authenticate(action: => Any) = { { @@ -102,3 +102,27 @@ } } } + +/** + * Allows only signed in users which can access the repository. + */ +trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService => + protected def readableUsersOnly(action: => Any) = { authenticate(action) } + protected def readableUsersOnly[T](action: T => Any) = (form: T) => authenticate({action(form)}) + + private def authenticate(action: => Any) = { + { + val paths = request.getRequestURI.split("/") + getRepository(paths(1), paths(2), baseUrl) match { + case None => NotFound() + case Some(repository) => context.loginAccount match { + case Some(x) if(x.isAdmin) => action + case Some(x) if(!repository.repository.isPrivate) => action + case Some(x) if(paths(1) == x.userName) => action + case Some(x) if(getCollaborators(paths(1), paths(2)).contains(x.userName)) => action + case _ => Unauthorized() + } + } + } + } +} diff --git a/src/main/twirl/issues/issue.scala.html b/src/main/twirl/issues/issue.scala.html index 0074136..8838305 100644 --- a/src/main/twirl/issues/issue.scala.html +++ b/src/main/twirl/issues/issue.scala.html @@ -145,9 +145,11 @@ $('a.assign').click(function(){ var userName = $(this).data('name'); - $.post('@url(repository)/issues/assign/@issue.issueId', { + $.post('@url(repository)/issues/@issue.issueId/assign', + { assignedUserName: userName - }, function(){ + }, + function(){ if(userName == ''){ $('#label-assigned').text('No one is assigned'); } else { @@ -161,9 +163,11 @@ $('a.milestone').click(function(){ var title = $(this).text(); var milestoneId = $(this).data('id'); - $.post('@url(repository)/issues/milestone/@issue.issueId', { + $.post('@url(repository)/issues/@issue.issueId/milestone', + { milestoneId: milestoneId - }, function(){ + }, + function(){ if(milestoneId == ''){ $('#label-milestone').text('No milestone'); } else {