mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-09 15:05:50 +01:00
(refs #115) Reorganize ssh sources
This commit is contained in:
@@ -1,150 +0,0 @@
|
|||||||
package servlet
|
|
||||||
|
|
||||||
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
|
||||||
import org.apache.sshd.SshServer
|
|
||||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
|
|
||||||
import org.apache.sshd.server._
|
|
||||||
import org.apache.sshd.server.session.ServerSession
|
|
||||||
import java.io.{OutputStream, InputStream}
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
|
||||||
import org.eclipse.jgit.api.Git
|
|
||||||
import util.Directory._
|
|
||||||
import util.ControlUtil._
|
|
||||||
import org.apache.sshd.server.command.UnknownCommand
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Start a SSH Service Daemon
|
|
||||||
*
|
|
||||||
* How to use ?
|
|
||||||
* git clone ssh://username@host_or_ip:29418/username/repository_name.git
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class SshServiceListener extends ServletContextListener {
|
|
||||||
// TODO SshServer should be controllable by admin view (start and stop)
|
|
||||||
// TODO Use Singleton object SshServer instance management
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[SshServiceListener])
|
|
||||||
|
|
||||||
val sshService = SshServer.setUpDefaultServer
|
|
||||||
|
|
||||||
// TODO allow to disable ssh feature(ssh feature requires many stability test)
|
|
||||||
val enableSsh = true
|
|
||||||
|
|
||||||
override def contextInitialized(sce: ServletContextEvent): Unit = {
|
|
||||||
if (!enableSsh) return
|
|
||||||
|
|
||||||
sshService.setPort(29418) // TODO must be configurable
|
|
||||||
|
|
||||||
// TODO PasswordAuthentication should be disable
|
|
||||||
// TODO Use PublicKeyAuthentication only => and It's stored db on account profile view
|
|
||||||
val authenticator = new MyPasswordAuthenticator
|
|
||||||
sshService.setPasswordAuthenticator(authenticator)
|
|
||||||
|
|
||||||
// TODO gitbucket.ser should be in GITBUCKET_HOME
|
|
||||||
sshService.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("gitbucket.ser"))
|
|
||||||
|
|
||||||
sshService.setCommandFactory(new CommandFactory {
|
|
||||||
override def createCommand(command: String): Command = {
|
|
||||||
logger.info(s"command: String -> ${command}")
|
|
||||||
command match {
|
|
||||||
case s if s.startsWith("git-upload-pack ") => new GitUploadPack(command)
|
|
||||||
case s if s.startsWith("git-receive-pack") => new GitReceivePack(command)
|
|
||||||
case _ => new UnknownCommand(command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
sshService.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
override def contextDestroyed(sce: ServletContextEvent): Unit = {
|
|
||||||
sshService.stop(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// always true authenticator... TODO Implements PublicKeyAuthenticator
|
|
||||||
class MyPasswordAuthenticator extends PasswordAuthenticator {
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[MyPasswordAuthenticator])
|
|
||||||
|
|
||||||
override def authenticate(username: String, password: String, session: ServerSession): Boolean = {
|
|
||||||
logger.info("noop authenticate!!!")
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class GitCommand(val command: String) extends Command {
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[GitCommand])
|
|
||||||
val (gitCommand, owner, repositoryName) = parseCommand
|
|
||||||
var err: OutputStream = null
|
|
||||||
var in: InputStream = null
|
|
||||||
var out: OutputStream = null
|
|
||||||
var callback: ExitCallback = null
|
|
||||||
|
|
||||||
def runnable: Runnable
|
|
||||||
|
|
||||||
override def start(env: Environment): Unit = {
|
|
||||||
logger.info(s"start command : ${command}")
|
|
||||||
logger.info(s"parsed command : ${gitCommand}, ${owner}, ${repositoryName}")
|
|
||||||
val thread = new Thread(runnable)
|
|
||||||
thread.start
|
|
||||||
}
|
|
||||||
|
|
||||||
override def destroy(): Unit = {
|
|
||||||
}
|
|
||||||
|
|
||||||
override def setExitCallback(callback: ExitCallback): Unit = {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
override def setErrorStream(err: OutputStream): Unit = {
|
|
||||||
this.err = err
|
|
||||||
}
|
|
||||||
|
|
||||||
override def setOutputStream(out: OutputStream): Unit = {
|
|
||||||
this.out = out
|
|
||||||
}
|
|
||||||
|
|
||||||
override def setInputStream(in: InputStream): Unit = {
|
|
||||||
this.in = in
|
|
||||||
}
|
|
||||||
|
|
||||||
private def parseCommand: Tuple3[String, String, String] = {
|
|
||||||
// command sample: git-upload-pack '/username/repository_name.git'
|
|
||||||
// command sample: git-receive-pack '/username/repository_name.git'
|
|
||||||
// TODO This is not correct....
|
|
||||||
val splitted = command.split(" ")
|
|
||||||
val gitCommand = splitted(0)
|
|
||||||
val gitUser = splitted(1).substring(1, splitted(1).length - 5).split("/")(1)
|
|
||||||
val gitRepo = splitted(1).substring(1, splitted(1).length - 5).split("/")(2)
|
|
||||||
(gitCommand, gitUser, gitRepo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class GitUploadPack(command: String) extends GitCommand(command: String) {
|
|
||||||
override val runnable = new Runnable {
|
|
||||||
override def run(): Unit = {
|
|
||||||
using(Git.open(getRepositoryDir(owner, repositoryName))) {
|
|
||||||
git =>
|
|
||||||
val repository = git.getRepository
|
|
||||||
val upload = new UploadPack(repository)
|
|
||||||
upload.upload(in, out, err)
|
|
||||||
callback.onExit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GitReceivePack(command: String) extends GitCommand(command: String) {
|
|
||||||
override val runnable = new Runnable {
|
|
||||||
override def run(): Unit = {
|
|
||||||
using(Git.open(getRepositoryDir(owner, repositoryName))) {
|
|
||||||
git =>
|
|
||||||
val repository = git.getRepository
|
|
||||||
val receive = new ReceivePack(repository)
|
|
||||||
// TODO ReceivePack has many options. Need more check.
|
|
||||||
receive.receive(in, out, err)
|
|
||||||
callback.onExit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
101
src/main/scala/ssh/GitCommand.scala
Normal file
101
src/main/scala/ssh/GitCommand.scala
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import org.apache.sshd.server.{CommandFactory, Environment, ExitCallback, Command}
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.{InputStream, OutputStream}
|
||||||
|
import util.ControlUtil._
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import util.Directory._
|
||||||
|
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
||||||
|
import org.apache.sshd.server.command.UnknownCommand
|
||||||
|
|
||||||
|
|
||||||
|
class GitCommandFactory extends CommandFactory {
|
||||||
|
private val logger = LoggerFactory.getLogger(classOf[GitCommandFactory])
|
||||||
|
|
||||||
|
override def createCommand(command: String): Command = {
|
||||||
|
logger.info(s"command: String -> " + command)
|
||||||
|
command match {
|
||||||
|
// TODO MUST use regular expression and UnitTest
|
||||||
|
case s if s.startsWith("git-upload-pack") => new GitUploadPack(command)
|
||||||
|
case s if s.startsWith("git-receive-pack") => new GitReceivePack(command)
|
||||||
|
case _ => new UnknownCommand(command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GitCommand(val command: String) extends Command {
|
||||||
|
private val logger = LoggerFactory.getLogger(classOf[GitCommand])
|
||||||
|
protected val (gitCommand, owner, repositoryName) = parseCommand
|
||||||
|
protected var err: OutputStream = null
|
||||||
|
protected var in: InputStream = null
|
||||||
|
protected var out: OutputStream = null
|
||||||
|
protected var callback: ExitCallback = null
|
||||||
|
|
||||||
|
protected def runnable: Runnable
|
||||||
|
|
||||||
|
override def start(env: Environment): Unit = {
|
||||||
|
logger.info(s"start command : " + command)
|
||||||
|
logger.info(s"parsed command : $gitCommand, $owner, $repositoryName")
|
||||||
|
val thread = new Thread(runnable)
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def destroy(): Unit = {
|
||||||
|
}
|
||||||
|
|
||||||
|
override def setExitCallback(callback: ExitCallback): Unit = {
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
override def setErrorStream(err: OutputStream): Unit = {
|
||||||
|
this.err = err
|
||||||
|
}
|
||||||
|
|
||||||
|
override def setOutputStream(out: OutputStream): Unit = {
|
||||||
|
this.out = out
|
||||||
|
}
|
||||||
|
|
||||||
|
override def setInputStream(in: InputStream): Unit = {
|
||||||
|
this.in = in
|
||||||
|
}
|
||||||
|
|
||||||
|
private def parseCommand: (String, String, String) = {
|
||||||
|
// command sample: git-upload-pack '/username/repository_name.git'
|
||||||
|
// command sample: git-receive-pack '/username/repository_name.git'
|
||||||
|
// TODO This is not correct....
|
||||||
|
val split = command.split(" ")
|
||||||
|
val gitCommand = split(0)
|
||||||
|
val gitUser = split(1).substring(1, split(1).length - 5).split("/")(1)
|
||||||
|
val gitRepo = split(1).substring(1, split(1).length - 5).split("/")(2)
|
||||||
|
(gitCommand, gitUser, gitRepo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GitUploadPack(command: String) extends GitCommand(command: String) {
|
||||||
|
override def runnable = new Runnable {
|
||||||
|
override def run(): Unit = {
|
||||||
|
using(Git.open(getRepositoryDir(owner, repositoryName))) { git =>
|
||||||
|
val repository = git.getRepository
|
||||||
|
val upload = new UploadPack(repository)
|
||||||
|
upload.upload(in, out, err)
|
||||||
|
callback.onExit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GitReceivePack(command: String) extends GitCommand(command: String) {
|
||||||
|
override def runnable = new Runnable {
|
||||||
|
override def run(): Unit = {
|
||||||
|
using(Git.open(getRepositoryDir(owner, repositoryName))) { git =>
|
||||||
|
val repository = git.getRepository
|
||||||
|
// TODO hook commit
|
||||||
|
val receive = new ReceivePack(repository)
|
||||||
|
receive.receive(in, out, err)
|
||||||
|
callback.onExit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
24
src/main/scala/ssh/PublicKeyAuthenticator.scala
Normal file
24
src/main/scala/ssh/PublicKeyAuthenticator.scala
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import org.apache.sshd.server.{PublickeyAuthenticator, PasswordAuthenticator}
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.apache.sshd.server.session.ServerSession
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
|
|
||||||
|
class PublicKeyAuthenticator extends PublickeyAuthenticator {
|
||||||
|
override def authenticate(username: String, key: PublicKey, session: ServerSession): Boolean = {
|
||||||
|
// TODO Implements PublicKeyAuthenticator
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// always true authenticator...
|
||||||
|
class MyPasswordAuthenticator extends PasswordAuthenticator {
|
||||||
|
private val logger = LoggerFactory.getLogger(classOf[MyPasswordAuthenticator])
|
||||||
|
|
||||||
|
override def authenticate(username: String, password: String, session: ServerSession): Boolean = {
|
||||||
|
logger.info("noop authenticate!!!")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/scala/ssh/SshServerListener.scala
Normal file
59
src/main/scala/ssh/SshServerListener.scala
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
||||||
|
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
|
||||||
|
object SshServer {
|
||||||
|
private val logger = LoggerFactory.getLogger(SshServer.getClass)
|
||||||
|
|
||||||
|
val DEFAULT_PORT: Int = 29418 // TODO read from config
|
||||||
|
val SSH_SERVICE_ENABLE = true
|
||||||
|
|
||||||
|
private val server = org.apache.sshd.SshServer.setUpDefaultServer()
|
||||||
|
|
||||||
|
|
||||||
|
private def configure() = {
|
||||||
|
server.setPort(DEFAULT_PORT)
|
||||||
|
|
||||||
|
// TODO not password use PublicKeyAuthenticator
|
||||||
|
val authenticator = new MyPasswordAuthenticator
|
||||||
|
server.setPasswordAuthenticator(authenticator)
|
||||||
|
|
||||||
|
// TODO gitbucket.ser should be in GITBUCKET_HOME
|
||||||
|
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("gitbucket.ser"))
|
||||||
|
server.setCommandFactory(new GitCommandFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
def start() = {
|
||||||
|
if (SSH_SERVICE_ENABLE) {
|
||||||
|
configure()
|
||||||
|
server.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def stop() = {
|
||||||
|
server.stop(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a SSH Service Daemon
|
||||||
|
*
|
||||||
|
* How to use ?
|
||||||
|
* git clone ssh://username@host_or_ip:29418/username/repository_name.git
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class SshServerListener extends ServletContextListener {
|
||||||
|
override def contextInitialized(sce: ServletContextEvent): Unit = {
|
||||||
|
SshServer.start
|
||||||
|
}
|
||||||
|
|
||||||
|
override def contextDestroyed(sce: ServletContextEvent): Unit = {
|
||||||
|
SshServer.stop
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<!-- SSH Daemon initialized -->
|
<!-- SSH Daemon initialized -->
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>servlet.SshServiceListener</listener-class>
|
<listener-class>ssh.SshServerListener</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
|
|||||||
Reference in New Issue
Block a user