github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/keys_test.go (about)

     1  /*
     2   * Copyright 2018-2022 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package cmd
    17  
    18  import (
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/nats-io/nkeys"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/nats-io/nsc/v2/cmd/store"
    28  )
    29  
    30  func storeOldCreds(ts *TestStore, operator string, account string, user string, data []byte) error {
    31  	ks := filepath.Join(ts.Dir, "keys")
    32  	target := filepath.Join(ks, operator, "accounts", account, "users", fmt.Sprintf("%s.creds", user))
    33  	return os.WriteFile(target, []byte(user), 0700)
    34  
    35  }
    36  
    37  func storeOldKey(ts *TestStore, operator string, account string, user string, seed []byte) error {
    38  	// old key layout was:
    39  	// <op>/<op>.nk
    40  	// <op>/accounts/<actname>/<actname>.nk
    41  	// <op>/accounts/<actname>/users/<un>.creds
    42  	// <op>/accounts/<actname>/users/<un>.nk
    43  	// new key layout is:
    44  	// keys/<op>/<first letter pk>/<second and third letters pk>/<pk>.nk
    45  	// creds/<op>/<actname>/<un>.creds
    46  	kp, err := store.ExtractSeed(string(seed))
    47  	if err != nil {
    48  		return err
    49  	}
    50  	prefix, err := store.KeyType(kp)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	ks := filepath.Join(ts.Dir, "keys")
    55  	var target string
    56  	switch prefix {
    57  	case nkeys.PrefixByteOperator:
    58  		target = filepath.Join(ks, operator, fmt.Sprintf("%s.nk", operator))
    59  	case nkeys.PrefixByteAccount:
    60  		target = filepath.Join(ks, operator, "accounts", account, fmt.Sprintf("%s.nk", account))
    61  	case nkeys.PrefixByteUser:
    62  		target = filepath.Join(ks, operator, "accounts", account, "users", fmt.Sprintf("%s.nk", user))
    63  	}
    64  
    65  	if err := os.MkdirAll(filepath.Dir(target), 0700); err != nil {
    66  		return err
    67  	}
    68  
    69  	return os.WriteFile(target, seed, 0700)
    70  }
    71  
    72  func Test_HasOldStructure(t *testing.T) {
    73  	ts := NewEmptyStore(t)
    74  	defer ts.Done(t)
    75  
    76  	ks := filepath.Join(ts.Dir, "keys")
    77  	old := store.KeyStorePath
    78  	store.KeyStorePath = ks
    79  
    80  	oseed, _, _ := CreateOperatorKey(t)
    81  	require.NoError(t, storeOldKey(ts, "O", "", "", oseed))
    82  	require.FileExists(t, filepath.Join(ks, "O", "O.nk"))
    83  
    84  	aseed, _, _ := CreateAccountKey(t)
    85  	require.NoError(t, storeOldKey(ts, "O", "A", "", aseed))
    86  	require.FileExists(t, filepath.Join(ks, "O", "accounts", "A", "A.nk"))
    87  
    88  	useed, _, _ := CreateUserKey(t)
    89  	require.NoError(t, storeOldKey(ts, "O", "A", "U", useed))
    90  	require.FileExists(t, filepath.Join(ks, "O", "accounts", "A", "users", "U.nk"))
    91  
    92  	err := storeOldCreds(ts, "O", "A", "U", []byte("user"))
    93  	require.NoError(t, err)
    94  
    95  	isOld, err := store.IsOldKeyRing(store.GetKeysDir())
    96  	require.NoError(t, err)
    97  	require.True(t, isOld)
    98  	store.KeyStorePath = old
    99  }
   100  
   101  func Test_MigrateKeys(t *testing.T) {
   102  	ts := NewEmptyStore(t)
   103  	defer ts.Done(t)
   104  
   105  	ks := filepath.Join(ts.Dir, "keys")
   106  	oldKs := store.KeyStorePath
   107  	store.KeyStorePath = ks
   108  
   109  	oseed, opk, _ := CreateOperatorKey(t)
   110  	require.NoError(t, storeOldKey(ts, "O", "", "", oseed))
   111  	require.FileExists(t, filepath.Join(ks, "O", "O.nk"))
   112  
   113  	aseed, apk, _ := CreateAccountKey(t)
   114  	require.NoError(t, storeOldKey(ts, "O", "A", "", aseed))
   115  	require.FileExists(t, filepath.Join(ks, "O", "accounts", "A", "A.nk"))
   116  
   117  	useed, upk, _ := CreateUserKey(t)
   118  	require.NoError(t, storeOldKey(ts, "O", "A", "U", useed))
   119  	require.FileExists(t, filepath.Join(ks, "O", "accounts", "A", "users", "U.nk"))
   120  
   121  	err := storeOldCreds(ts, "O", "A", "U", []byte("user"))
   122  	require.NoError(t, err)
   123  
   124  	needsUpdate, err := store.KeysNeedMigration()
   125  	require.NoError(t, err)
   126  	require.True(t, needsUpdate)
   127  
   128  	old, err := store.Migrate()
   129  	require.NoError(t, err)
   130  	require.DirExists(t, old)
   131  
   132  	// directory for keystore has a "keys" and "creds"
   133  	keysDir := filepath.Join(ks, "keys")
   134  	require.FileExists(t, filepath.Join(keysDir, "O", opk[1:3], fmt.Sprintf("%s.nk", opk)))
   135  	require.FileExists(t, filepath.Join(keysDir, "A", apk[1:3], fmt.Sprintf("%s.nk", apk)))
   136  	require.FileExists(t, filepath.Join(keysDir, "U", upk[1:3], fmt.Sprintf("%s.nk", upk)))
   137  
   138  	credsDir := filepath.Join(ks, "creds")
   139  	cf := filepath.Join(credsDir, "O", "A", "U.creds")
   140  	require.FileExists(t, cf)
   141  
   142  	for _, pk := range []string{opk, apk, upk} {
   143  		kp, err := ts.KeyStore.GetKeyPair(pk)
   144  		require.NoError(t, err)
   145  		require.NotNil(t, kp)
   146  	}
   147  	store.KeyStorePath = oldKs
   148  }