diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala index 90fe415..bdbd898 100644 --- a/src/main/scala/ScalatraBootstrap.scala +++ b/src/main/scala/ScalatraBootstrap.scala @@ -14,6 +14,7 @@ context.getFilterRegistration("basicAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/git/*") // Register controllers + context.mount(new AnonymousAccessController, "/*") context.mount(new IndexController, "/") context.mount(new SearchController, "/") context.mount(new FileUploadController, "/upload") diff --git a/src/main/scala/app/AnonymousAccessController.scala b/src/main/scala/app/AnonymousAccessController.scala new file mode 100644 index 0000000..35481ab --- /dev/null +++ b/src/main/scala/app/AnonymousAccessController.scala @@ -0,0 +1,14 @@ +package app + +class AnonymousAccessController extends AnonymousAccessControllerBase + +trait AnonymousAccessControllerBase extends ControllerBase { + get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) { + if(!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") && + !context.currentPath.startsWith("/register")) { + Unauthorized() + } else { + pass() + } + } +} diff --git a/src/main/scala/app/SystemSettingsController.scala b/src/main/scala/app/SystemSettingsController.scala index 5c11959..e278433 100644 --- a/src/main/scala/app/SystemSettingsController.scala +++ b/src/main/scala/app/SystemSettingsController.scala @@ -16,6 +16,7 @@ "baseUrl" -> trim(label("Base URL", optional(text()))), "information" -> trim(label("Information", optional(text()))), "allowAccountRegistration" -> trim(label("Account registration", boolean())), + "allowAnonymousAccess" -> trim(label("Anonymous access", boolean())), "isCreateRepoOptionPublic" -> trim(label("Default option to create a new repository", boolean())), "gravatar" -> trim(label("Gravatar", boolean())), "notification" -> trim(label("Notification", boolean())), diff --git a/src/main/scala/service/SystemSettingsService.scala b/src/main/scala/service/SystemSettingsService.scala index 7575094..156a23b 100644 --- a/src/main/scala/service/SystemSettingsService.scala +++ b/src/main/scala/service/SystemSettingsService.scala @@ -14,6 +14,7 @@ settings.baseUrl.foreach(x => props.setProperty(BaseURL, x.replaceFirst("/\\Z", ""))) settings.information.foreach(x => props.setProperty(Information, x)) props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString) + props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString) props.setProperty(IsCreateRepoOptionPublic, settings.isCreateRepoOptionPublic.toString) props.setProperty(Gravatar, settings.gravatar.toString) props.setProperty(Notification, settings.notification.toString) @@ -65,6 +66,7 @@ getOptionValue[String](props, BaseURL, None).map(x => x.replaceFirst("/\\Z", "")), getOptionValue[String](props, Information, None), getValue(props, AllowAccountRegistration, false), + getValue(props, AllowAnonymousAccess, true), getValue(props, IsCreateRepoOptionPublic, true), getValue(props, Gravatar, true), getValue(props, Notification, false), @@ -113,6 +115,7 @@ baseUrl: Option[String], information: Option[String], allowAccountRegistration: Boolean, + allowAnonymousAccess: Boolean, isCreateRepoOptionPublic: Boolean, gravatar: Boolean, notification: Boolean, @@ -158,6 +161,7 @@ private val BaseURL = "base_url" private val Information = "information" private val AllowAccountRegistration = "allow_account_registration" + private val AllowAnonymousAccess = "allow_anonymous_access" private val IsCreateRepoOptionPublic = "is_create_repository_option_public" private val Gravatar = "gravatar" private val Notification = "notification" diff --git a/src/main/scala/servlet/BasicAuthenticationFilter.scala b/src/main/scala/servlet/BasicAuthenticationFilter.scala index 8272c7a..cbecfc1 100644 --- a/src/main/scala/servlet/BasicAuthenticationFilter.scala +++ b/src/main/scala/servlet/BasicAuthenticationFilter.scala @@ -28,33 +28,45 @@ override def setCharacterEncoding(encoding: String) = {} } + val isUpdating = request.getRequestURI.endsWith("/git-receive-pack") || "service=git-receive-pack".equals(request.getQueryString) + + val settings = loadSystemSettings() + try { - defining(request.paths){ case Array(_, repositoryOwner, repositoryName, _*) => - getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match { - case Some(repository) => { - if(!request.getRequestURI.endsWith("/git-receive-pack") && - !"service=git-receive-pack".equals(request.getQueryString) && !repository.repository.isPrivate){ - chain.doFilter(req, wrappedResponse) - } else { - request.getHeader("Authorization") match { - case null => requireAuth(response) - case auth => decodeAuthHeader(auth).split(":") match { - case Array(username, password) => getWritableUser(username, password, repository) match { - case Some(account) => { - request.setAttribute(Keys.Request.UserName, account.userName) - chain.doFilter(req, wrappedResponse) + defining(request.paths){ + case Array(_, repositoryOwner, repositoryName, _*) => + getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match { + case Some(repository) => { + if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){ + chain.doFilter(req, wrappedResponse) + } else { + request.getHeader("Authorization") match { + case null => requireAuth(response) + case auth => decodeAuthHeader(auth).split(":") match { + case Array(username, password) => { + authenticate(settings, username, password) match { + case Some(account) => { + if(isUpdating && hasWritePermission(repository.owner, repository.name, Some(account))){ + request.setAttribute(Keys.Request.UserName, account.userName) + } + chain.doFilter(req, wrappedResponse) + } + case None => requireAuth(response) + } } - case None => requireAuth(response) + case _ => requireAuth(response) } - case _ => requireAuth(response) } } } + case None => { + logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.") + response.sendError(HttpServletResponse.SC_NOT_FOUND) + } } - case None => { - logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.") - response.sendError(HttpServletResponse.SC_NOT_FOUND) - } + case _ => { + logger.debug(s"Not enough path arguments: ${request.paths}") + response.sendError(HttpServletResponse.SC_NOT_FOUND) } } } catch { @@ -65,13 +77,6 @@ } } - private def getWritableUser(username: String, password: String, repository: RepositoryService.RepositoryInfo) - (implicit session: Session): Option[Account] = - authenticate(loadSystemSettings(), username, password) match { - case x @ Some(account) if(hasWritePermission(repository.owner, repository.name, x)) => x - case _ => None - } - private def requireAuth(response: HttpServletResponse): Unit = { response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"") response.sendError(HttpServletResponse.SC_UNAUTHORIZED) diff --git a/src/main/twirl/admin/system.scala.html b/src/main/twirl/admin/system.scala.html index 09c8ac8..194a164 100644 --- a/src/main/twirl/admin/system.scala.html +++ b/src/main/twirl/admin/system.scala.html @@ -66,6 +66,21 @@ + + +