diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index df4e712..b7d369e 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -45,9 +45,11 @@ "gravatar" -> trim(label("Gravatar", boolean())), "notification" -> trim(label("Notification", boolean())), "activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))), - "ssh" -> trim(label("SSH access", boolean())), - "sshHost" -> trim(label("SSH host", optional(text()))), - "sshPort" -> trim(label("SSH port", optional(number()))), + "ssh" -> mapping( + "enabled" -> trim(label("SSH access", boolean())), + "host" -> trim(label("SSH host", optional(text()))), + "port" -> trim(label("SSH port", optional(number()))), + )(Ssh.apply), "useSMTP" -> trim(label("SMTP", boolean())), "smtp" -> optionalIfNotChecked( "useSMTP", @@ -91,13 +93,16 @@ )(OIDC.apply) ), "skinName" -> trim(label("AdminLTE skin name", text(required))), - "showMailAddress" -> trim(label("Show mail address", boolean())) + "showMailAddress" -> trim(label("Show mail address", boolean())), + "pluginNetworkInstall" -> new SingleValueType[Boolean] { + override def convert(value: String, messages: Messages): Boolean = context.settings.pluginNetworkInstall + } )(SystemSettings.apply).verifying { settings => Vector( - if (settings.ssh && settings.baseUrl.isEmpty) { + if (settings.ssh.enabled && settings.baseUrl.isEmpty) { Some("baseUrl" -> "Base URL is required if SSH access is enabled.") } else None, - if (settings.ssh && settings.sshHost.isEmpty) { + if (settings.ssh.enabled && settings.ssh.sshHost.isEmpty) { Some("sshHost" -> "SSH host is required if SSH access is enabled.") } else None ).flatten @@ -325,25 +330,27 @@ val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion // Plugins in the remote repository - val repositoryPlugins = PluginRepository - .getPlugins() - .map { meta => - (meta, meta.versions.reverse.find { version => - gitbucketVersion == version.gitbucketVersion && !enabledPlugins.exists { plugin => - plugin.pluginId == meta.id && plugin.pluginVersion == version.version - } - }) - } - .collect { - case (meta, Some(version)) => - new PluginInfoBase( - pluginId = meta.id, - pluginName = meta.name, - pluginVersion = version.version, - gitbucketVersion = Some(version.gitbucketVersion), - description = meta.description - ) - } + val repositoryPlugins = if (context.settings.pluginNetworkInstall) { + PluginRepository + .getPlugins() + .map { meta => + (meta, meta.versions.reverse.find { version => + gitbucketVersion == version.gitbucketVersion && !enabledPlugins.exists { plugin => + plugin.pluginId == meta.id && plugin.pluginVersion == version.version + } + }) + } + .collect { + case (meta, Some(version)) => + new PluginInfoBase( + pluginId = meta.id, + pluginName = meta.name, + pluginVersion = version.version, + gitbucketVersion = Some(version.gitbucketVersion), + description = meta.description + ) + } + } else Nil // Merge val plugins = (enabledPlugins.map((_, true)) ++ repositoryPlugins.map((_, false))) @@ -359,12 +366,17 @@ }) post("/admin/plugins/_reload")(adminOnly { + // Update configuration + val pluginNetworkInstall = params.get("pluginNetworkInstall").map(_.toBoolean).getOrElse(false) + saveSystemSettings(context.settings.copy(pluginNetworkInstall = pluginNetworkInstall)) + + // Reload plugins PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn) flash += "info" -> "All plugins were reloaded." redirect("/admin/plugins") }) - post("/admin/plugins/:pluginId/_uninstall")(adminOnly { // TODO Is version unnecessary? + post("/admin/plugins/:pluginId/_uninstall")(adminOnly { val pluginId = params("pluginId") if (PluginRegistry().getPlugins().exists(_.pluginId == pluginId)) { @@ -377,25 +389,28 @@ }) post("/admin/plugins/:pluginId/:version/_install")(adminOnly { - val pluginId = params("pluginId") - val version = params("version") + if (context.settings.pluginNetworkInstall) { + val pluginId = params("pluginId") + val version = params("version") - PluginRepository - .getPlugins() - .collect { case meta if meta.id == pluginId => (meta, meta.versions.find(_.version == version)) } - .foreach { - case (meta, version) => - version.foreach { version => - PluginRegistry.install( - pluginId, - new java.net.URL(version.url), - request.getServletContext, - loadSystemSettings(), - request2Session(request).conn - ) - flash += "info" -> s"${pluginId}:${version.version} was installed." - } - } + PluginRepository + .getPlugins() + .collect { case meta if meta.id == pluginId => (meta, meta.versions.find(_.version == version)) } + .foreach { + case (meta, version) => + version.foreach { version => + PluginRegistry.install( + pluginId, + new java.net.URL(version.url), + request.getServletContext, + loadSystemSettings(), + request2Session(request).conn + ) + flash += "info" -> s"${pluginId}:${version.version} was installed." + } + } + } + redirect("/admin/plugins") }) diff --git a/src/main/scala/gitbucket/core/plugin/PluginRepository.scala b/src/main/scala/gitbucket/core/plugin/PluginRepository.scala index 39a909f..92f9a35 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRepository.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRepository.scala @@ -15,7 +15,6 @@ lazy val LocalRepositoryIndexFile = new java.io.File(LocalRepositoryDir, "plugins.json") def getPlugins(): Seq[PluginMetadata] = { - // TODO Pre-load the plugin list in background val url = new java.net.URL("https://plugins.gitbucket-community.org/releases/plugins.json") val str = IOUtils.toString(url, "UTF-8") parsePluginJson(str) diff --git a/src/main/scala/gitbucket/core/service/RepositoryService.scala b/src/main/scala/gitbucket/core/service/RepositoryService.scala index 5381b40..0ff127f 100644 --- a/src/main/scala/gitbucket/core/service/RepositoryService.scala +++ b/src/main/scala/gitbucket/core/service/RepositoryService.scala @@ -772,7 +772,7 @@ def httpUrl(owner: String, name: String)(implicit context: Context): String = s"${context.baseUrl}/git/${owner}/${name}.git" def sshUrl(owner: String, name: String)(implicit context: Context): Option[String] = - if (context.settings.ssh) { + if (context.settings.ssh.enabled) { context.settings.sshAddress.map { x => s"ssh://${x.genericUser}@${x.host}:${x.port}/${owner}/${name}.git" } diff --git a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala index 7ac861e..df9109a 100644 --- a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala +++ b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala @@ -23,9 +23,9 @@ props.setProperty(Gravatar, settings.gravatar.toString) props.setProperty(Notification, settings.notification.toString) settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString)) - props.setProperty(Ssh, settings.ssh.toString) - settings.sshHost.foreach(x => props.setProperty(SshHost, x.trim)) - settings.sshPort.foreach(x => props.setProperty(SshPort, x.toString)) + props.setProperty(SshEnabled, settings.ssh.enabled.toString) + settings.ssh.sshHost.foreach(x => props.setProperty(SshHost, x.trim)) + settings.ssh.sshPort.foreach(x => props.setProperty(SshPort, x.toString)) props.setProperty(UseSMTP, settings.useSMTP.toString) if (settings.useSMTP) { settings.smtp.foreach { smtp => @@ -69,6 +69,8 @@ } props.setProperty(SkinName, settings.skinName.toString) props.setProperty(ShowMailAddress, settings.showMailAddress.toString) + props.setProperty(PluginNetworkInstall, settings.pluginNetworkInstall.toString) + using(new java.io.FileOutputStream(GitBucketConf)) { out => props.store(out, null) } @@ -91,9 +93,11 @@ getValue(props, Gravatar, false), getValue(props, Notification, false), getOptionValue[Int](props, ActivityLogLimit, None), - getValue(props, Ssh, false), - getOptionValue[String](props, SshHost, None).map(_.trim), - getOptionValue(props, SshPort, Some(DefaultSshPort)), + Ssh( + getValue(props, SshEnabled, false), + getOptionValue[String](props, SshHost, None).map(_.trim), + getOptionValue(props, SshPort, Some(DefaultSshPort)) + ), getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP if (getValue(props, UseSMTP, getValue(props, Notification, false))) { Some( @@ -146,7 +150,8 @@ None }, getValue(props, SkinName, "skin-blue"), - getValue(props, ShowMailAddress, false) + getValue(props, ShowMailAddress, false), + getValue(props, PluginNetworkInstall, false) ) } } @@ -167,9 +172,7 @@ gravatar: Boolean, notification: Boolean, activityLogLimit: Option[Int], - ssh: Boolean, - sshHost: Option[String], - sshPort: Option[Int], + ssh: Ssh, useSMTP: Boolean, smtp: Option[Smtp], ldapAuthentication: Boolean, @@ -177,7 +180,8 @@ oidcAuthentication: Boolean, oidc: Option[OIDC], skinName: String, - showMailAddress: Boolean + showMailAddress: Boolean, + pluginNetworkInstall: Boolean ) { def baseUrl(request: HttpServletRequest): String = @@ -196,12 +200,18 @@ .fold(base)(_ + base.dropWhile(_ != ':')) } - def sshAddress: Option[SshAddress] = sshHost.collect { - case host if ssh => - SshAddress(host, sshPort.getOrElse(DefaultSshPort), "git") + def sshAddress: Option[SshAddress] = ssh.sshHost.collect { + case host if ssh.enabled => + SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git") } } + case class Ssh( + enabled: Boolean, + sshHost: Option[String], + sshPort: Option[Int] + ) + case class Ldap( host: String, port: Option[Int], @@ -255,7 +265,7 @@ private val Gravatar = "gravatar" private val Notification = "notification" private val ActivityLogLimit = "activity_log_limit" - private val Ssh = "ssh" + private val SshEnabled = "ssh" private val SshHost = "ssh.host" private val SshPort = "ssh.port" private val UseSMTP = "useSMTP" @@ -287,6 +297,7 @@ private val OidcJwsAlgorithm = "oidc.jws_algorithm" private val SkinName = "skinName" private val ShowMailAddress = "showMailAddress" + private val PluginNetworkInstall = "plugin.networkInstall" private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = { getSystemProperty(key).getOrElse(getEnvironmentVariable(key).getOrElse { diff --git a/src/main/twirl/gitbucket/core/account/menu.scala.html b/src/main/twirl/gitbucket/core/account/menu.scala.html index 16e0341..07fc46b 100644 --- a/src/main/twirl/gitbucket/core/account/menu.scala.html +++ b/src/main/twirl/gitbucket/core/account/menu.scala.html @@ -19,7 +19,7 @@ Profile - @if(context.settings.ssh){ + @if(context.settings.ssh.enabled){