Skip to content

Commit 087f497

Browse files
authored
Few fixes to SSH and Owner to implement auth and transfer commands in Spago (#695)
1 parent ebf4944 commit 087f497

File tree

9 files changed

+74
-40
lines changed

9 files changed

+74
-40
lines changed

app/src/App/Auth.purs

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ type SignAuthenticated =
3333

3434
signPayload :: SignAuthenticated -> Either String SSH.Signature
3535
signPayload { privateKey, rawPayload } = do
36-
private <- SSH.parsePrivateKey privateKey
36+
private <- lmap SSH.printPrivateKeyParseError $ SSH.parsePrivateKey { key: privateKey, passphrase: Nothing }
3737
pure $ SSH.sign private rawPayload

flake.lock

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/Owner.purs

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ newtype Owner = Owner
2525
}
2626

2727
derive instance Newtype Owner _
28-
derive newtype instance Eq Owner
28+
29+
-- | Owners are equal if their keytype and public key are equal, regardless of
30+
-- | the id field, which is just an arbitrary string.
31+
instance Eq Owner where
32+
eq (Owner o1) (Owner o2) = o1.keytype == o2.keytype && o1.public == o2.public
2933

3034
-- | A codec for encoding and decoding an `Owner` as JSON. Represented as a JSON
3135
-- | object.

lib/src/SSH.js

+8
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ export function isPrivateKeyImpl(parsedKey) {
2828
export function equalsImpl(a, b) {
2929
return a.equals(b);
3030
}
31+
32+
export function publicToOwnerImpl(parsedKey) {
33+
return {
34+
id: parsedKey.comment,
35+
keytype: parsedKey.type,
36+
public: parsedKey.getPublicSSH().toString("base64"),
37+
};
38+
}

lib/src/SSH.purs

+31-13
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
module Registry.SSH
22
( PublicKey
33
, PrivateKey
4+
, PrivateKeyParseError(..)
5+
, printPrivateKeyParseError
46
, Signature(..)
57
, parsePublicKey
68
, parsePrivateKey
7-
, parsePrivateKeyWithPassword
9+
, publicKeyToOwner
810
, sign
911
, verify
1012
) where
1113

1214
import Prelude
1315

16+
import Data.Bifunctor (bimap)
1417
import Data.Either (Either(..))
1518
import Data.Function.Uncurried (Fn1, Fn2, Fn3, Fn4, runFn1, runFn2, runFn3, runFn4)
19+
import Data.Maybe (Maybe)
1620
import Data.Newtype (class Newtype)
17-
import Data.Nullable (Nullable, notNull, null)
21+
import Data.Nullable (Nullable, null)
22+
import Data.Nullable as Nullable
1823
import Effect.Exception as Exception
24+
import Registry.Owner (Owner(..))
1925

2026
-- | A parsed SSH public key which can be used to verify payloads.
2127
newtype PublicKey = PublicKey ParsedKey
@@ -37,18 +43,23 @@ foreign import parseKeyImpl :: forall r. Fn4 (Exception.Error -> r) (ParsedKey -
3743
parse :: String -> Either String ParsedKey
3844
parse buf = runFn4 parseKeyImpl (Left <<< Exception.message) Right buf null
3945

40-
-- | Parse a non-password-protected private SSH key
41-
parsePrivateKey :: String -> Either String PrivateKey
42-
parsePrivateKey key = case parse key of
43-
Right parsed | not (isPrivateKey parsed) -> Left $ "Expected private key, but this is a public key of type " <> keyType parsed
44-
result -> map PrivateKey result
46+
data PrivateKeyParseError
47+
= GotPublicKeyInstead String
48+
| RequiresPassphrase
49+
| OtherParseError String
4550

46-
-- | Parse a password-protected private SSH key
47-
parsePrivateKeyWithPassword :: { key :: String, passphrase :: String } -> Either String PrivateKey
48-
parsePrivateKeyWithPassword { key, passphrase } =
49-
case runFn4 parseKeyImpl (Left <<< Exception.message) Right key (notNull passphrase) of
50-
Right parsed | not (isPrivateKey parsed) -> Left $ "Expected private key, but this is a public key of type " <> keyType parsed
51-
result -> map PrivateKey result
51+
printPrivateKeyParseError :: PrivateKeyParseError -> String
52+
printPrivateKeyParseError = case _ of
53+
GotPublicKeyInstead keyType' -> "Expected private key, but got public key of type " <> keyType'
54+
RequiresPassphrase -> "Encrypted private key requires a passphrase"
55+
OtherParseError message -> message
56+
57+
parsePrivateKey :: { key :: String, passphrase :: Maybe String } -> Either PrivateKeyParseError PrivateKey
58+
parsePrivateKey { key, passphrase } =
59+
case runFn4 parseKeyImpl (Left <<< Exception.message) Right key (Nullable.toNullable passphrase) of
60+
Right parsed | not (isPrivateKey parsed) -> Left $ GotPublicKeyInstead $ keyType parsed
61+
Left "Encrypted private OpenSSH key detected, but no passphrase given" -> Left RequiresPassphrase
62+
result -> bimap OtherParseError PrivateKey result
5263

5364
-- | Parse a public SSH key
5465
parsePublicKey :: String -> Either String PublicKey
@@ -88,3 +99,10 @@ isPrivateKey :: ParsedKey -> Boolean
8899
isPrivateKey = runFn1 isPrivateKeyImpl
89100

90101
foreign import equalsImpl :: Fn2 ParsedKey ParsedKey Boolean
102+
103+
foreign import publicToOwnerImpl :: Fn1 PublicKey { keytype :: String, public :: String, id :: Nullable String }
104+
105+
publicKeyToOwner :: PublicKey -> Owner
106+
publicKeyToOwner key = do
107+
let { id: nullableId, keytype, public } = runFn1 publicToOwnerImpl key
108+
Owner { keytype, public, id: Nullable.toMaybe nullableId }

lib/test/Registry/SSH.purs

+10-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Test.Registry.SSH (spec) where
33
import Prelude
44

55
import Data.Either (Either(..))
6+
import Data.Maybe (Maybe(..))
67
import Data.String as String
78
import Registry.SSH (Signature(..))
89
import Registry.SSH as SSH
@@ -13,8 +14,8 @@ import Test.Spec as Spec
1314
spec :: Spec.Spec Unit
1415
spec = do
1516
Spec.it "Parses an ED25519 private key" do
16-
case SSH.parsePrivateKey id_ed25519 of
17-
Left err -> Assert.fail $ "Failed to parse ed_25519 private key: " <> err
17+
case SSH.parsePrivateKey { key: id_ed25519, passphrase: Nothing } of
18+
Left err -> Assert.fail $ "Failed to parse ed_25519 private key: " <> SSH.printPrivateKeyParseError err
1819
Right _ -> pure unit
1920

2021
Spec.it "Parses an ED25519 public key" do
@@ -23,12 +24,13 @@ spec = do
2324
Right _ -> pure unit
2425

2526
Spec.it "Parses a password-protected RSA private key" do
26-
case SSH.parsePrivateKey id_rsa of
27-
Left err1 -> do
28-
err1 `Assert.shouldEqual` "Encrypted private OpenSSH key detected, but no passphrase given"
29-
case SSH.parsePrivateKeyWithPassword { key: id_rsa, passphrase: id_rsa_password } of
30-
Left err2 -> Assert.fail $ "Failed to parse id_rsa private key with password: " <> err2
27+
case SSH.parsePrivateKey { key: id_rsa, passphrase: Nothing } of
28+
Left err1@SSH.RequiresPassphrase -> do
29+
SSH.printPrivateKeyParseError err1 `Assert.shouldEqual` "Encrypted private key requires a passphrase"
30+
case SSH.parsePrivateKey { key: id_rsa, passphrase: Just id_rsa_password } of
31+
Left err2 -> Assert.fail $ "Failed to parse id_rsa private key with password: " <> SSH.printPrivateKeyParseError err2
3132
Right _ -> pure unit
33+
Left otherError -> Assert.fail $ "Should have required a passphrase, but got: " <> SSH.printPrivateKeyParseError otherError
3234
Right _ -> Assert.fail $ "Expected parse failure, but got key."
3335

3436
Spec.it "Parses an RSA public key" do
@@ -41,7 +43,7 @@ spec = do
4143
Left _ -> pure unit
4244
Right _ -> Assert.fail "Parsed private key as a public key."
4345

44-
case SSH.parsePrivateKey id_ed25519_pub of
46+
case SSH.parsePrivateKey { key: id_ed25519_pub, passphrase: Nothing } of
4547
Left _ -> pure unit
4648
Right _ -> Assert.fail "Parsed public key as a private key."
4749

lib/test/Registry/Test/Utils.purs

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ unsafeSSHPublicKey str = fromRight ("Failed to parse SSH key: " <> str) (SSH.par
9191

9292
-- | Unsafely parse a private SSH key from a string
9393
unsafeSSHPrivateKey :: String -> SSH.PrivateKey
94-
unsafeSSHPrivateKey str = fromRight ("Failed to parse SSH key: " <> str) (SSH.parsePrivateKey str)
94+
unsafeSSHPrivateKey str = fromRight ("Failed to parse SSH key: " <> str) (SSH.parsePrivateKey { key: str, passphrase: Nothing })
9595

9696
-- | Unsafely create a manifest from a name, version, and array of dependencies
9797
-- | where keys are package names and values are ranges.

spago.lock

+14-12
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ workspace:
201201
- variant
202202
test_dependencies: []
203203
package_set:
204-
registry: 46.0.0
204+
registry: 50.10.0
205205
extra_packages:
206206
codec-json: 1.2.0
207207
dodo-printer:
@@ -568,10 +568,11 @@ packages:
568568
- prelude
569569
encoding:
570570
type: registry
571-
version: 0.0.8
572-
integrity: sha256-n0HhENAax0yr7JFwZXcisx0jJvVf1dFwqd+Q5i2Pr88=
571+
version: 0.0.9
572+
integrity: sha256-vtyUO06Jww8pFl4wRekPd1YpJl2XuQXcaNXQgHtG8Tk=
573573
dependencies:
574574
- arraybuffer-types
575+
- effect
575576
- either
576577
- exceptions
577578
- functions
@@ -608,16 +609,18 @@ packages:
608609
- unsafe-coerce
609610
fetch:
610611
type: registry
611-
version: 4.0.0
612-
integrity: sha256-Ita74WPIvzCsSIkUQQbBDKgIrsnuBWIRzEJ8Q5P7iQU=
612+
version: 4.1.0
613+
integrity: sha256-zCwBUkRL9n6nUhK1+7UqqsuxswPFATsZfGSBOA3NYYY=
613614
dependencies:
614615
- aff
615616
- arraybuffer-types
616617
- bifunctors
617618
- effect
619+
- either
618620
- foreign
619621
- http-methods
620622
- js-fetch
623+
- js-promise
621624
- js-promise-aff
622625
- maybe
623626
- newtype
@@ -1354,8 +1357,8 @@ packages:
13541357
- tuples
13551358
ordered-collections:
13561359
type: registry
1357-
version: 3.1.1
1358-
integrity: sha256-boSYHmlz4aSbwsNN4VxiwCStc0t+y1F7BXmBS+1JNtI=
1360+
version: 3.2.0
1361+
integrity: sha256-o9jqsj5rpJmMdoe/zyufWHFjYYFTTsJpgcuCnqCO6PM=
13591362
dependencies:
13601363
- arrays
13611364
- foldable-traversable
@@ -1452,8 +1455,8 @@ packages:
14521455
dependencies: []
14531456
profunctor:
14541457
type: registry
1455-
version: 6.0.0
1456-
integrity: sha256-99NzxFgTr4CGlCSRYG1kShL+JhYbihhHtbOk1/0R5zI=
1458+
version: 6.0.1
1459+
integrity: sha256-E58hSYdJvF2Qjf9dnWLPlJKh2Z2fLfFLkQoYi16vsFk=
14571460
dependencies:
14581461
- control
14591462
- distributive
@@ -1616,8 +1619,8 @@ packages:
16161619
- unsafe-coerce
16171620
spec:
16181621
type: registry
1619-
version: 7.5.5
1620-
integrity: sha256-HdyBH7Ys1/m2SdTq3u2u9LdQ4cGeaohWeEMYay2mHdU=
1622+
version: 7.6.0
1623+
integrity: sha256-+merGdQbL9zWONbnt8S8J9afGJ59MQqGtS0qSd3yu4I=
16211624
dependencies:
16221625
- aff
16231626
- ansi
@@ -1626,7 +1629,6 @@ packages:
16261629
- bifunctors
16271630
- control
16281631
- datetime
1629-
- debug
16301632
- effect
16311633
- either
16321634
- exceptions

spago.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
workspace:
22
lock: true
33
package_set:
4-
registry: 46.0.0
4+
registry: 50.10.0
55
extra_packages:
66
codec-json: 1.2.0
77
dodo-printer:

0 commit comments

Comments
 (0)