20241: Move authorized_keys to new code path, validate public key.
[arvados.git] / lib / controller / localdb / authorized_key_test.go
diff --git a/lib/controller/localdb/authorized_key_test.go b/lib/controller/localdb/authorized_key_test.go
new file mode 100644 (file)
index 0000000..44fa3cf
--- /dev/null
@@ -0,0 +1,114 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package localdb
+
+import (
+       _ "embed"
+       "errors"
+       "io/ioutil"
+       "net/http"
+       "os"
+       "strings"
+
+       "git.arvados.org/arvados.git/sdk/go/arvados"
+       "git.arvados.org/arvados.git/sdk/go/arvadostest"
+       "git.arvados.org/arvados.git/sdk/go/httpserver"
+       . "gopkg.in/check.v1"
+)
+
+var _ = Suite(&authorizedKeySuite{})
+
+type authorizedKeySuite struct {
+       localdbSuite
+}
+
+//go:embed testdata/rsa.pub
+var testPubKey string
+
+func (s *authorizedKeySuite) TestAuthorizedKeyCreate(c *C) {
+       ak, err := s.localdb.AuthorizedKeyCreate(s.userctx, arvados.CreateOptions{
+               Attrs: map[string]interface{}{
+                       "name":     "testkey",
+                       "key_type": "SSH",
+               }})
+       c.Assert(err, IsNil)
+       c.Check(ak.KeyType, Equals, "SSH")
+       defer s.localdb.AuthorizedKeyDelete(s.userctx, arvados.DeleteOptions{UUID: ak.UUID})
+       updated, err := s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+               UUID:  ak.UUID,
+               Attrs: map[string]interface{}{"name": "testkeyrenamed"}})
+       c.Check(err, IsNil)
+       c.Check(updated.UUID, Equals, ak.UUID)
+       c.Check(updated.Name, Equals, "testkeyrenamed")
+       c.Check(updated.ModifiedByUserUUID, Equals, arvadostest.ActiveUserUUID)
+
+       _, err = s.localdb.AuthorizedKeyCreate(s.userctx, arvados.CreateOptions{
+               Attrs: map[string]interface{}{
+                       "name":       "testkey",
+                       "public_key": "ssh-dsa boguskey\n",
+               }})
+       c.Check(err, ErrorMatches, `Public key does not appear to be valid: ssh: no key found`)
+       _, err = s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+               UUID: ak.UUID,
+               Attrs: map[string]interface{}{
+                       "public_key": strings.Replace(testPubKey, "A", "#", 1),
+               }})
+       c.Check(err, ErrorMatches, `Public key does not appear to be valid: ssh: no key found`)
+       _, err = s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+               UUID: ak.UUID,
+               Attrs: map[string]interface{}{
+                       "public_key": testPubKey + testPubKey,
+               }})
+       c.Check(err, ErrorMatches, `Public key does not appear to be valid: extra data after key`)
+       _, err = s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+               UUID: ak.UUID,
+               Attrs: map[string]interface{}{
+                       "public_key": testPubKey + "# extra data\n",
+               }})
+       c.Check(err, ErrorMatches, `Public key does not appear to be valid: extra data after key`)
+       _, err = s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+               UUID: ak.UUID,
+               Attrs: map[string]interface{}{
+                       "public_key": strings.Replace(testPubKey, "ssh-rsa", "ssh-dsa", 1),
+               }})
+       c.Check(err, ErrorMatches, `Public key does not appear to be valid: leading type field "ssh-dsa" does not match actual key type "ssh-rsa"`)
+       var se httpserver.HTTPStatusError
+       if c.Check(errors.As(err, &se), Equals, true) {
+               c.Check(se.HTTPStatus(), Equals, http.StatusBadRequest)
+       }
+
+       dirents, err := os.ReadDir("./testdata")
+       c.Assert(err, IsNil)
+       c.Assert(dirents, Not(HasLen), 0)
+       for _, dirent := range dirents {
+               if !strings.HasSuffix(dirent.Name(), ".pub") {
+                       continue
+               }
+               pubkeyfile := "./testdata/" + dirent.Name()
+               c.Logf("checking public key from %s", pubkeyfile)
+               pubkey, err := ioutil.ReadFile(pubkeyfile)
+               if !c.Check(err, IsNil) {
+                       continue
+               }
+               updated, err := s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+                       UUID: ak.UUID,
+                       Attrs: map[string]interface{}{
+                               "public_key": string(pubkey),
+                       }})
+               c.Check(err, IsNil)
+               c.Check(updated.PublicKey, Equals, string(pubkey))
+
+               _, err = s.localdb.AuthorizedKeyUpdate(s.userctx, arvados.UpdateOptions{
+                       UUID: ak.UUID,
+                       Attrs: map[string]interface{}{
+                               "public_key": strings.Replace(string(pubkey), " ", "-bogus ", 1),
+                       }})
+               c.Check(err, ErrorMatches, `.*type field ".*" does not match actual key type ".*"`)
+       }
+
+       deleted, err := s.localdb.AuthorizedKeyDelete(s.userctx, arvados.DeleteOptions{UUID: ak.UUID})
+       c.Check(err, IsNil)
+       c.Check(deleted.UUID, Equals, ak.UUID)
+}