mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-01-01 13:20:03 +01:00
Merge branch 'master' into fix-slick-stackoverflow
This commit is contained in:
@@ -4,8 +4,9 @@ import com.typesafe.sbt.pgp.PgpKeys._
|
||||
val Organization = "io.github.gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val GitBucketVersion = "4.27.0"
|
||||
val ScalatraVersion = "2.6.1"
|
||||
val JettyVersion = "9.4.7.v20170914"
|
||||
val ScalatraVersion = "2.6.3"
|
||||
val JettyVersion = "9.4.11.v20180605"
|
||||
val JgitVersion = "5.0.2.201807311906-r"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
||||
@@ -30,8 +31,8 @@ resolvers ++= Seq(
|
||||
)
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit.http.server" % "5.0.1.201806211838-r",
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % "5.0.1.201806211838-r",
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit.http.server" % JgitVersion,
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % JgitVersion,
|
||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=1.1.6
|
||||
sbt.version=1.2.0
|
||||
|
||||
@@ -94,9 +94,16 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
),
|
||||
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
||||
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
||||
"pluginNetworkInstall" -> new SingleValueType[Boolean] {
|
||||
override def convert(value: String, messages: Messages): Boolean = context.settings.pluginNetworkInstall
|
||||
}
|
||||
"pluginNetworkInstall" -> trim(label("Network plugin installation", boolean())),
|
||||
"proxy" -> optionalIfNotChecked(
|
||||
"useProxy",
|
||||
mapping(
|
||||
"host" -> trim(label("Proxy host", text(required))),
|
||||
"port" -> trim(label("Proxy port", number())),
|
||||
"user" -> trim(label("Keystore", optional(text()))),
|
||||
"password" -> trim(label("Keystore", optional(text())))
|
||||
)(Proxy.apply)
|
||||
)
|
||||
)(SystemSettings.apply).verifying { settings =>
|
||||
Vector(
|
||||
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
||||
@@ -380,11 +387,6 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
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")
|
||||
|
||||
@@ -6,8 +6,8 @@ import java.nio.file.{Files, Paths, StandardWatchEventKinds}
|
||||
import java.util.Base64
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.servlet.ServletContext
|
||||
|
||||
import javax.servlet.ServletContext
|
||||
import com.github.zafarkhaja.semver.Version
|
||||
import gitbucket.core.controller.{Context, ControllerBase}
|
||||
import gitbucket.core.model.{Account, Issue}
|
||||
@@ -18,10 +18,12 @@ import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.DatabaseConfig
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.HttpClientUtil._
|
||||
import io.github.gitbucket.solidbase.Solidbase
|
||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||
import io.github.gitbucket.solidbase.model.Module
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.http.client.methods.HttpGet
|
||||
import org.apache.sshd.server.Command
|
||||
import org.slf4j.LoggerFactory
|
||||
import play.twirl.api.Html
|
||||
@@ -253,8 +255,17 @@ object PluginRegistry {
|
||||
})
|
||||
.foreach(_.delete())
|
||||
|
||||
val in = url.openStream()
|
||||
FileUtils.copyToFile(in, new File(PluginHome, new File(url.getFile).getName))
|
||||
withHttpClient(settings.pluginProxy) { httpClient =>
|
||||
val httpGet = new HttpGet(url.toString)
|
||||
try {
|
||||
val response = httpClient.execute(httpGet)
|
||||
val in = response.getEntity.getContent
|
||||
FileUtils.copyToFile(in, new File(PluginHome, new File(url.getFile).getName))
|
||||
} finally {
|
||||
httpGet.releaseConnection()
|
||||
}
|
||||
}
|
||||
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package gitbucket.core.plugin
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import gitbucket.core.util.HttpClientUtil._
|
||||
import org.json4s._
|
||||
import org.apache.commons.io.IOUtils
|
||||
|
||||
import org.apache.http.client.methods.HttpGet
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
object PluginRepository {
|
||||
@@ -12,18 +17,28 @@ object PluginRepository {
|
||||
org.json4s.jackson.JsonMethods.parse(json).extract[Seq[PluginMetadata]]
|
||||
}
|
||||
|
||||
def getPlugins(): Seq[PluginMetadata] = {
|
||||
def getPlugins()(implicit context: Context): Seq[PluginMetadata] = {
|
||||
try {
|
||||
val url = new java.net.URL("https://plugins.gitbucket-community.org/releases/plugins.json")
|
||||
val str = IOUtils.toString(url, "UTF-8")
|
||||
parsePluginJson(str)
|
||||
|
||||
withHttpClient(context.settings.pluginProxy) { httpClient =>
|
||||
val httpGet = new HttpGet(url.toString)
|
||||
try {
|
||||
val response = httpClient.execute(httpGet)
|
||||
using(response.getEntity.getContent) { in =>
|
||||
val str = IOUtils.toString(in, "UTF-8")
|
||||
parsePluginJson(str)
|
||||
}
|
||||
} finally {
|
||||
httpGet.releaseConnection()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case t: Throwable =>
|
||||
logger.warn("Failed to access to the plugin repository: " + t.toString)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Mapped from plugins.json
|
||||
|
||||
@@ -70,6 +70,16 @@ trait SystemSettingsService {
|
||||
props.setProperty(SkinName, settings.skinName.toString)
|
||||
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
||||
props.setProperty(PluginNetworkInstall, settings.pluginNetworkInstall.toString)
|
||||
settings.pluginProxy.foreach { proxy =>
|
||||
props.setProperty(PluginProxyHost, proxy.host)
|
||||
props.setProperty(PluginProxyPort, proxy.port.toString)
|
||||
proxy.user.foreach { user =>
|
||||
props.setProperty(PluginProxyUser, user)
|
||||
}
|
||||
proxy.password.foreach { password =>
|
||||
props.setProperty(PluginProxyPassword, password)
|
||||
}
|
||||
}
|
||||
|
||||
using(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||
props.store(out, null)
|
||||
@@ -112,9 +122,7 @@ trait SystemSettingsService {
|
||||
getOptionValue(props, SmtpFromName, None)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
} else None,
|
||||
getValue(props, LdapAuthentication, false),
|
||||
if (getValue(props, LdapAuthentication, false)) {
|
||||
Some(
|
||||
@@ -133,9 +141,7 @@ trait SystemSettingsService {
|
||||
getOptionValue(props, LdapKeystore, None)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
} else None,
|
||||
getValue(props, OidcAuthentication, false),
|
||||
if (getValue(props, OidcAuthentication, false)) {
|
||||
Some(
|
||||
@@ -151,7 +157,17 @@ trait SystemSettingsService {
|
||||
},
|
||||
getValue(props, SkinName, "skin-blue"),
|
||||
getValue(props, ShowMailAddress, false),
|
||||
getValue(props, PluginNetworkInstall, false)
|
||||
getValue(props, PluginNetworkInstall, false),
|
||||
if (getValue(props, PluginProxyHost, "").nonEmpty) {
|
||||
Some(
|
||||
Proxy(
|
||||
getValue(props, PluginProxyHost, ""),
|
||||
getValue(props, PluginProxyPort, 8080),
|
||||
getOptionValue(props, PluginProxyUser, None),
|
||||
getOptionValue(props, PluginProxyPassword, None)
|
||||
)
|
||||
)
|
||||
} else None
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -181,7 +197,8 @@ object SystemSettingsService {
|
||||
oidc: Option[OIDC],
|
||||
skinName: String,
|
||||
showMailAddress: Boolean,
|
||||
pluginNetworkInstall: Boolean
|
||||
pluginNetworkInstall: Boolean,
|
||||
pluginProxy: Option[Proxy]
|
||||
) {
|
||||
|
||||
def baseUrl(request: HttpServletRequest): String =
|
||||
@@ -249,6 +266,13 @@ object SystemSettingsService {
|
||||
fromName: Option[String]
|
||||
)
|
||||
|
||||
case class Proxy(
|
||||
host: String,
|
||||
port: Int,
|
||||
user: Option[String],
|
||||
password: Option[String],
|
||||
)
|
||||
|
||||
case class SshAddress(host: String, port: Int, genericUser: String)
|
||||
|
||||
case class Lfs(serverUrl: Option[String])
|
||||
@@ -298,6 +322,10 @@ object SystemSettingsService {
|
||||
private val SkinName = "skinName"
|
||||
private val ShowMailAddress = "showMailAddress"
|
||||
private val PluginNetworkInstall = "plugin.networkInstall"
|
||||
private val PluginProxyHost = "plugin.proxy.host"
|
||||
private val PluginProxyPort = "plugin.proxy.port"
|
||||
private val PluginProxyUser = "plugin.proxy.user"
|
||||
private val PluginProxyPassword = "plugin.proxy.password"
|
||||
|
||||
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
||||
getSystemProperty(key).getOrElse(getEnvironmentVariable(key).getOrElse {
|
||||
|
||||
35
src/main/scala/gitbucket/core/util/HttpClientUtil.scala
Normal file
35
src/main/scala/gitbucket/core/util/HttpClientUtil.scala
Normal file
@@ -0,0 +1,35 @@
|
||||
package gitbucket.core.util
|
||||
|
||||
import gitbucket.core.service.SystemSettingsService
|
||||
import org.apache.http.HttpHost
|
||||
import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
|
||||
import org.apache.http.impl.client.{BasicCredentialsProvider, CloseableHttpClient, HttpClientBuilder}
|
||||
|
||||
object HttpClientUtil {
|
||||
|
||||
def withHttpClient[T](proxy: Option[SystemSettingsService.Proxy])(f: CloseableHttpClient => T): T = {
|
||||
val builder = HttpClientBuilder.create.useSystemProperties
|
||||
|
||||
proxy.foreach { proxy =>
|
||||
builder.setProxy(new HttpHost(proxy.host, proxy.port))
|
||||
|
||||
for (user <- proxy.user; password <- proxy.password) {
|
||||
val credential = new BasicCredentialsProvider()
|
||||
credential.setCredentials(
|
||||
new AuthScope(proxy.host, proxy.port),
|
||||
new UsernamePasswordCredentials(user, password)
|
||||
)
|
||||
builder.setDefaultCredentialsProvider(credential)
|
||||
}
|
||||
}
|
||||
|
||||
val httpClient = builder.build()
|
||||
|
||||
try {
|
||||
f(httpClient)
|
||||
} finally {
|
||||
httpClient.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
@gitbucket.core.admin.html.menu("plugins") {
|
||||
@gitbucket.core.helper.html.information(info)
|
||||
<form action="@context.path/admin/plugins/_reload" method="POST" class="pull-right">
|
||||
<input type="checkbox" name="pluginNetworkInstall" id="pluginNetworkInstall" value="true" @if(context.settings.pluginNetworkInstall){checked}>
|
||||
<label for="pluginNetworkInstall">Install plugin from <a href="https://plugins.gitbucket-community.org" target="_blank">plugins.gitbucket-community.org</a></label>
|
||||
<input type="submit" value="Reload plugins" class="btn btn-default">
|
||||
</form>
|
||||
<h1 class="system-settings-title">Plugins</h1>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<ul class="nav nav-tabs fill-width" id="pullreq-tab">
|
||||
<li><a href="#system">System settings</a></li>
|
||||
<li><a href="#authentication">Authentication</a></li>
|
||||
<li><a href="#plugins">Plugins</a></li>
|
||||
</ul>
|
||||
<div class="tab-content fill-width" style="padding-top: 20px;">
|
||||
<div class="tab-pane" id="system">
|
||||
@@ -16,6 +17,9 @@
|
||||
<div class="tab-pane" id="authentication">
|
||||
@settings_authentication(info)
|
||||
</div>
|
||||
<div class="tab-pane" id="plugins">
|
||||
@settings_plugins(info)
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="align-right" style="margin-top: 20px;">
|
||||
@@ -30,6 +34,9 @@ $(function(){
|
||||
if(location.hash == '#authentication'){
|
||||
$('li:has(a[href="#authentication"])').addClass('active');
|
||||
$('div#authentication').addClass('active');
|
||||
} else if(location.hash == '#plugins'){
|
||||
$('li:has(a[href="#plugins"])').addClass('active');
|
||||
$('div#plugins').addClass('active');
|
||||
} else {
|
||||
$('li:has(a[href="#system"])').addClass('active');
|
||||
$('div#system').addClass('active');
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
@(info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||
<label class="strong">Plugin repositories</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="pluginNetworkInstall" name="pluginNetworkInstall"@if(context.settings.pluginNetworkInstall){ checked} />
|
||||
<a href="https://plugins.gitbucket-community.org" target="_blank">plugins.gitbucket-community.org</a>
|
||||
</label>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="useProxy" name="useProxy"@if(context.settings.pluginProxy.isDefined){ checked} />
|
||||
Use proxy
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="proxy">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyHost">Proxy host</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyHost" name="proxy.host" class="form-control" value="@context.settings.pluginProxy.map(_.host)"/>
|
||||
<span id="error-proxy_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyPort">Proxy port</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyPort" name="proxy.port" class="form-control input-mini" value="@context.settings.pluginProxy.map(_.port)"/>
|
||||
<span id="error-proxy_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyUser">Proxy user</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyUser" name="proxy.user" class="form-control" value="@context.settings.pluginProxy.map(_.user)"/>
|
||||
<span id="error-proxy_user" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="proxyPassword">Proxy password</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="proxyPassword" name="proxy.password" class="form-control" value="@context.settings.pluginProxy.map(_.password)"/>
|
||||
<span id="error-proxy_password" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#useProxy').change(function(){
|
||||
$('.proxy input').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
@@ -138,7 +138,8 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
|
||||
oidc = None,
|
||||
skinName = "skin-blue",
|
||||
showMailAddress = false,
|
||||
pluginNetworkInstall = false
|
||||
pluginNetworkInstall = false,
|
||||
pluginProxy = None
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user