Skip to content

Commit 3795c1e

Browse files
committed
Merge branch 'release/0.6.0'
2 parents 82404c6 + 32b916f commit 3795c1e

File tree

10 files changed

+108
-31
lines changed

10 files changed

+108
-31
lines changed

CHANGELOG

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Version 0.6.0 (2012-05-30)
2+
--------------------------
3+
- Upgraded to
4+
- sshj 0.8.0
5+
- jzlib 1.1.1
6+
- Added option for loading the private key file from the classpath
7+
- Disabled cross-path publishing
8+
9+
10+
Version 0.5.0 (2012-02-06)
11+
--------------------------
12+
first public release

README.markdown

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ It builds on [sshj] to provide the following features:
1111

1212
## Installation
1313

14-
The current release is *0.5.0*, it's available from <http://repo.spray.cc>.
14+
The current release is *0.6.0*, it's available from <http://repo.spray.cc>.
1515
If you use [SBT] you can pull in the _scala-ssh_ artifacts with:
1616

1717
resolvers += "spray repo" at "http://repo.spray.cc"
1818

19-
libraryDependencies += "com.decodified" %% "scala-ssh" % "0.5.0"
19+
libraryDependencies += "com.decodified" %% "scala-ssh" % "0.6.0"
2020

2121
[sshj] uses [SLF4J] for logging, so you might want to also add [logback] to your dependencies:
2222

23-
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.0.0"
23+
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.0.3"
2424

2525
Additionally, in many cases you will need the following two artifacts, which provide additional cypher and compression
2626
support:
2727

2828
libraryDependencies ++= Seq(
2929
"org.bouncycastle" % "bcprov-jdk16" % "1.46",
30-
"com.jcraft" % "jzlib" % "1.0.7"
30+
"com.jcraft" % "jzlib" % "1.1.1"
3131
)
3232

3333

@@ -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`

build.sbt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name := "scala-ssh"
22

3-
version := "0.6.0-SNAPSHOT"
3+
version := "0.6.0"
44

55
organization := "com.decodified"
66

@@ -14,21 +14,17 @@ startYear := Some(2011)
1414

1515
licenses := Seq("Apache 2" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt"))
1616

17-
scalaVersion := "2.9.1"
17+
scalaVersion := "2.9.2"
1818

1919
scalacOptions := Seq("-deprecation", "-encoding", "utf8")
2020

2121
libraryDependencies ++= Seq(
22-
"net.schmizz" % "sshj" % "0.7.0",
22+
"net.schmizz" % "sshj" % "0.8.0",
2323
"org.slf4j" % "slf4j-api" % "1.6.4",
2424
"org.bouncycastle" % "bcprov-jdk16" % "1.46" % "provided",
25-
"com.jcraft" % "jzlib" % "1.0.7" % "provided",
26-
"org.specs2" %% "specs2" % "1.7.1" % "test",
27-
"ch.qos.logback" % "logback-classic" % "1.0.0" % "test"
28-
)
29-
30-
resolvers ++= Seq(
31-
"Akka repo" at "http://akka.io/repository/"
25+
"com.jcraft" % "jzlib" % "1.1.1" % "provided",
26+
"org.specs2" %% "specs2" % "1.10" % "test",
27+
"ch.qos.logback" % "logback-classic" % "1.0.3" % "test"
3228
)
3329

3430

@@ -40,6 +36,8 @@ credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")
4036

4137
publishMavenStyle := true
4238

39+
crossPaths := false
40+
4341
publishTo <<= version { version =>
4442
Some {
4543
"spray nexus" at {

notes/0.6.0.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
This is a maintenance release.
2+
Changes since 0.5.0:
3+
4+
- Upgraded to sshj 0.8.0 and jzlib 1.1.1
5+
- Added option for loading the private key file from the classpath
6+
- Disabled cross-path publishing

notes/about.markdown

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1-
_scala-ssh_ is a Scala library providing remote shell access via SSH.
1+
[scala-ssh](https://github.com/sirthias/scala-ssh) is a Scala library providing remote shell access via SSH.
2+
It builds on [sshj](https://github.com/shikhar/sshj) to provide the following features:
3+
4+
* Remote execution of one or more shell commands
5+
* Access to `stdin`, `stdout`, `stderr` and exitcode of remote shell commands
6+
* Authentication via password or public key
7+
* Host key verification via `known_hosts` file or explicit fingerprint
8+
* Convenient configuration of remote host properties via config file, resource or directly in code
9+
* Scala-idiomatic API

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=0.11.2
1+
sbt.version=0.11.3

project/plugins.sbt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
addSbtPlugin("me.lessis" % "ls-sbt" % "0.1.1")
22

33
resolvers ++= Seq(
4-
"less is" at "http://repo.lessis.me",
4+
Resolver.url("sbt-plugin-releases", new URL(
5+
"http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/"))(
6+
Resolver.ivyStylePatterns),
57
"coda" at "http://repo.codahale.com"
68
)

src/main/ls/0.6.0.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
{
3+
"organization":"com.decodified",
4+
"name":"scala-ssh",
5+
"version":"0.6.0",
6+
"description":"A Scala library providing remote shell access via SSH",
7+
"site":"https://github.com/sirthias/scala-ssh",
8+
"tags":["ssh"],
9+
"docs":"https://github.com/sirthias/scala-ssh/",
10+
"licenses": [{
11+
"name": "Apache 2",
12+
"url": "http://www.apache.org/licenses/LICENSE-2.0.txt"
13+
}],
14+
"resolvers": ["http://repo.spray.cc"],
15+
"dependencies": [{
16+
"organization":"net.schmizz",
17+
"name": "sshj",
18+
"version": "0.8.0"
19+
},{
20+
"organization":"org.slf4j",
21+
"name": "slf4j-api",
22+
"version": "1.6.4"
23+
},{
24+
"organization":"org.bouncycastle",
25+
"name": "bcprov-jdk16",
26+
"version": "1.46"
27+
},{
28+
"organization":"com.jcraft",
29+
"name": "jzlib",
30+
"version": "1.1.1"
31+
}],
32+
"scalas": ["2.9.2"],
33+
"sbt": false
34+
}

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)