Turn on / off network plugin installation

This commit is contained in:
Naoki Takezoe
2018-06-11 14:36:12 +09:00
parent ed7bb495ca
commit a4cebcc3ac
8 changed files with 99 additions and 72 deletions

View File

@@ -45,9 +45,11 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
"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 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
)(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 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
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 @@ 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")
})
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 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
})
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")
})

View File

@@ -15,7 +15,6 @@ object PluginRepository {
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)

View File

@@ -772,7 +772,7 @@ object RepositoryService {
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"
}

View File

@@ -23,9 +23,9 @@ trait SystemSettingsService {
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 @@ trait SystemSettingsService {
}
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 @@ trait SystemSettingsService {
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 @@ trait SystemSettingsService {
None
},
getValue(props, SkinName, "skin-blue"),
getValue(props, ShowMailAddress, false)
getValue(props, ShowMailAddress, false),
getValue(props, PluginNetworkInstall, false)
)
}
}
@@ -167,9 +172,7 @@ object SystemSettingsService {
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 @@ object SystemSettingsService {
oidcAuthentication: Boolean,
oidc: Option[OIDC],
skinName: String,
showMailAddress: Boolean
showMailAddress: Boolean,
pluginNetworkInstall: Boolean
) {
def baseUrl(request: HttpServletRequest): String =
@@ -196,12 +200,18 @@ object SystemSettingsService {
.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 @@ object SystemSettingsService {
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 @@ object SystemSettingsService {
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 {

View File

@@ -19,7 +19,7 @@
<i class="menu-icon octicon octicon-person"></i> <span>Profile</span>
</a>
</li>
@if(context.settings.ssh){
@if(context.settings.ssh.enabled){
<li class="menu-item-hover @if(active=="ssh"){active}">
<a href="@context.path/@userName/_ssh">
<i class="menu-icon octicon octicon-key"></i> <span>SSH Keys</span>

View File

@@ -3,14 +3,16 @@
@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>
@if(plugins.size > 0) {
<ul>
@plugins.map { case (plugin, enabled, updatableVersion) =>
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.pluginVersion</a></li>
}
@plugins.map { case (plugin, enabled, updatableVersion) =>
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.pluginVersion</a></li>
}
</ul>
@plugins.map { case (plugin, enabled, updatableVersion) =>

View File

@@ -182,7 +182,7 @@
<label class="strong">SSH access</label>
<fieldset>
<label class="checkbox">
<input type="checkbox" id="ssh" name="ssh"@if(context.settings.ssh){ checked}/>
<input type="checkbox" id="sshEnabled" name="ssh.enabled"@if(context.settings.ssh.enabled){ checked}/>
Enable SSH access to git repository
<span class="muted normal">(Both SSH host and Base URL are required if SSH access is enabled)</span>
</label>
@@ -191,15 +191,15 @@
<div class="form-group">
<label class="control-label col-md-2" for="sshHost">SSH host</label>
<div class="col-md-10">
<input type="text" id="sshHost" name="sshHost" class="form-control" value="@context.settings.sshHost"/>
<span id="error-sshHost" class="error"></span>
<input type="text" id="sshHost" name="ssh.host" class="form-control" value="@context.settings.ssh.sshHost"/>
<span id="error-ssh_host" class="error"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="sshPort">SSH port</label>
<div class="col-md-10">
<input type="text" id="sshPort" name="sshPort" class="form-control" value="@context.settings.sshPort"/>
<span id="error-sshPort" class="error"></span>
<input type="text" id="sshPort" name="ssh.port" class="form-control" value="@context.settings.ssh.sshPort"/>
<span id="error-ssh_port" class="error"></span>
</div>
</div>
</div>
@@ -329,7 +329,7 @@ $(function(){
}
});
$('#ssh').change(function(){
$('#sshEnabled').change(function(){
$('.ssh input').prop('disabled', !$(this).prop('checked'));
}).change();

View File

@@ -106,7 +106,7 @@
@gitbucket.core.helper.html.copy("repository-url", "repository-url-copy", forkedRepository.httpUrl){
<div class="input-group-btn" data-toggle="buttons">
<label class="btn btn-sm btn-default active" id="repository-url-http"><input type="radio" checked>HTTP</label>
@if(context.settings.ssh && context.loginAccount.isDefined){
@if(context.settings.ssh.enabled && context.loginAccount.isDefined){
<label class="btn btn-sm btn-default" id="repository-url-ssh"><input type="radio">SSH</label>
}
</div>