Skip to content

Commit b28c7ec

Browse files
committed
Add option for loading the private key file from the classpath
1 parent 7c83d22 commit b28c7ec

File tree

3 files changed

+31
-14
lines changed

3 files changed

+31
-14
lines changed

README.markdown

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ These key are defined:
9090
* `password`: required for login-type `password`, ignored otherwise
9191

9292
* `keyfile`: optionally specifies the location of the user keyfile to use with login-type `keyfile`,
93-
if not given the default files `~/.ssh/id_rsa` and `~/.ssh/id_dsa` are tried, ignored for login-type `password`
93+
if not given the default files `~/.ssh/id_rsa` and `~/.ssh/id_dsa` are tried, ignored for login-type `password`,
94+
if the filename starts with a `+` the file is searched in addition to the default locations,
95+
if the filename starts with `classpath:` it is interpreted as the name of a classpath resource holding the private key
9496

9597
* `passphrase`: optionally specifies the passphrase for the keyfile, if not given the keyfile is assumed to be
9698
unencrypted, ignored for login-type `password`

src/main/scala/com/decodified/scalassh/HostConfig.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ abstract class FromStringsHostConfigProvider extends HostConfigProvider {
100100
PublicKeyLogin(
101101
user,
102102
passphrase.map(SimplePasswordProducer),
103-
keyfile.map { kf =>
104-
if (kf.startsWith("+")) kf.tail :: DefaultKeyLocations else kf :: Nil
103+
keyfile.map {
104+
case kf if kf.startsWith("+") => kf.tail :: DefaultKeyLocations
105+
case kf => kf :: Nil
105106
}.getOrElse(DefaultKeyLocations)
106107
)
107108
}

src/main/scala/com/decodified/scalassh/SshClient.scala

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ package com.decodified.scalassh
1818

1919
import java.util.concurrent.TimeUnit
2020
import net.schmizz.sshj.SSHClient
21-
import java.io.File
2221
import org.slf4j.LoggerFactory
22+
import net.schmizz.sshj.userauth.keyprovider.KeyProvider
23+
import io.Source
24+
import java.io.{InputStream, FileNotFoundException, FileInputStream, File}
2325

2426
class SshClient(val config: HostConfig) {
2527
lazy val log = LoggerFactory.getLogger(getClass)
@@ -63,9 +65,26 @@ class SshClient(val config: HostConfig) {
6365
}
6466

6567
protected def authenticate(client: SSHClient) = {
66-
def existing(filenames: List[String]) = filenames.filter(new File(_).exists) match {
67-
case Nil => sys.error("None of the configured keyfiles exists: " + filenames.mkString(", "))
68-
case files => files
68+
def keyProviders(locations: List[String], passProducer: PasswordProducer): List[KeyProvider] = {
69+
def inputStream(location: String): Option[InputStream] = {
70+
if (location.startsWith("classpath:")) {
71+
val resource = location.substring("classpath:".length)
72+
Option(getClass.getClassLoader.getResourceAsStream(resource))
73+
.orElse(throw new RuntimeException("Classpath resource '" + resource + "' containing private key could not be found"))
74+
} else {
75+
try Some(new FileInputStream(location))
76+
catch { case _: FileNotFoundException => None }
77+
}
78+
}
79+
locations.flatMap { location =>
80+
inputStream(location).map { stream =>
81+
val privateKey = Source.fromInputStream(stream).getLines().mkString("\n")
82+
client.loadKeys(privateKey, null, passProducer)
83+
}
84+
} match {
85+
case Nil => sys.error("None of the configured keyfiles exists: " + locations.mkString(", "))
86+
case x => x
87+
}
6988
}
7089

7190
require(client.isConnected && !client.isAuthenticated)
@@ -76,14 +95,9 @@ class SshClient(val config: HostConfig) {
7695
client.authPassword(user, passProducer)
7796
client
7897
}
79-
case PublicKeyLogin(user, None, keyfileLocations) =>
98+
case PublicKeyLogin(user, passProducer, keyfileLocations) =>
8099
protect("Could not authenticate (with keyfile) to") {
81-
client.authPublickey(user, existing(keyfileLocations): _*)
82-
client
83-
}
84-
case PublicKeyLogin(user, Some(passProducer), keyfileLocations) =>
85-
protect("Could not authenticate (with encrypted keyfile) to") {
86-
client.authPublickey(user, existing(keyfileLocations).map(client.loadKeys(_, passProducer)): _*)
100+
client.authPublickey(user, keyProviders(keyfileLocations, passProducer.getOrElse(null)): _*)
87101
client
88102
}
89103
}

0 commit comments

Comments
 (0)