github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/keys/policy_test.go (about)

     1  /*
     2  Copyright 2022 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package keys_test
    18  
    19  import (
    20  	"fmt"
    21  	"slices"
    22  	"testing"
    23  
    24  	"github.com/gravitational/trace"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/gravitational/teleport/api/utils/keys"
    28  )
    29  
    30  var (
    31  	privateKeyPolicies = []keys.PrivateKeyPolicy{
    32  		keys.PrivateKeyPolicyNone,
    33  		keys.PrivateKeyPolicyHardwareKey,
    34  		keys.PrivateKeyPolicyHardwareKeyTouch,
    35  		keys.PrivateKeyPolicyHardwareKeyPIN,
    36  		keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    37  		keys.PrivateKeyPolicyWebSession,
    38  	}
    39  	hardwareKeyPolicies = []keys.PrivateKeyPolicy{
    40  		keys.PrivateKeyPolicyHardwareKey,
    41  		keys.PrivateKeyPolicyHardwareKeyTouch,
    42  		keys.PrivateKeyPolicyHardwareKeyPIN,
    43  		keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    44  		keys.PrivateKeyPolicyWebSession,
    45  	}
    46  	hardwareKeyTouchPolicies = []keys.PrivateKeyPolicy{
    47  		keys.PrivateKeyPolicyHardwareKeyTouch,
    48  		keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    49  		keys.PrivateKeyPolicyWebSession,
    50  	}
    51  	hardwareKeyPINPolicies = []keys.PrivateKeyPolicy{
    52  		keys.PrivateKeyPolicyHardwareKeyPIN,
    53  		keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    54  		keys.PrivateKeyPolicyWebSession,
    55  	}
    56  	hardwareKeyTouchAndPINPolicies = []keys.PrivateKeyPolicy{
    57  		keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    58  		keys.PrivateKeyPolicyWebSession,
    59  	}
    60  )
    61  
    62  func TestIsRequiredPolicyMet(t *testing.T) {
    63  	privateKeyPolicies := []keys.PrivateKeyPolicy{
    64  		keys.PrivateKeyPolicyNone,
    65  		keys.PrivateKeyPolicyHardwareKey,
    66  		keys.PrivateKeyPolicyHardwareKeyTouch,
    67  		keys.PrivateKeyPolicyHardwareKeyPIN,
    68  		keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    69  	}
    70  	for _, tc := range []struct {
    71  		requiredPolicy     keys.PrivateKeyPolicy
    72  		satisfyingPolicies []keys.PrivateKeyPolicy
    73  	}{
    74  		{
    75  			requiredPolicy:     keys.PrivateKeyPolicyNone,
    76  			satisfyingPolicies: privateKeyPolicies,
    77  		}, {
    78  			requiredPolicy:     keys.PrivateKeyPolicyHardwareKey,
    79  			satisfyingPolicies: hardwareKeyPolicies,
    80  		}, {
    81  			requiredPolicy:     keys.PrivateKeyPolicyHardwareKeyTouch,
    82  			satisfyingPolicies: hardwareKeyTouchPolicies,
    83  		}, {
    84  			requiredPolicy:     keys.PrivateKeyPolicyHardwareKeyPIN,
    85  			satisfyingPolicies: hardwareKeyPINPolicies,
    86  		}, {
    87  			requiredPolicy:     keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
    88  			satisfyingPolicies: hardwareKeyTouchAndPINPolicies,
    89  		},
    90  	} {
    91  		t.Run(string(tc.requiredPolicy), func(t *testing.T) {
    92  			for _, keyPolicy := range privateKeyPolicies {
    93  				if tc.requiredPolicy.IsSatisfiedBy(keyPolicy) {
    94  					require.Contains(t, tc.satisfyingPolicies, keyPolicy, "Policy %q does not meet %q but IsRequirePolicyMet(%v, %v) returned true", keyPolicy, tc.requiredPolicy, tc.requiredPolicy, keyPolicy)
    95  				} else {
    96  					require.NotContains(t, tc.satisfyingPolicies, keyPolicy, "Policy %q does meet %q but IsRequirePolicyMet(%v, %v) returned false", keyPolicy, tc.requiredPolicy, tc.requiredPolicy, keyPolicy)
    97  				}
    98  			}
    99  		})
   100  	}
   101  }
   102  
   103  func TestGetPolicyFromSet(t *testing.T) {
   104  	testCases := []struct {
   105  		name       string
   106  		policySet  []keys.PrivateKeyPolicy
   107  		wantPolicy keys.PrivateKeyPolicy
   108  	}{
   109  		{
   110  			name: "none",
   111  			policySet: []keys.PrivateKeyPolicy{
   112  				keys.PrivateKeyPolicyNone,
   113  				keys.PrivateKeyPolicyNone,
   114  			},
   115  			wantPolicy: keys.PrivateKeyPolicyNone,
   116  		}, {
   117  			name: "hardware key policy",
   118  			policySet: []keys.PrivateKeyPolicy{
   119  				keys.PrivateKeyPolicyNone,
   120  				keys.PrivateKeyPolicyHardwareKey,
   121  			},
   122  			wantPolicy: keys.PrivateKeyPolicyHardwareKey,
   123  		}, {
   124  			name: "touch policy",
   125  			policySet: []keys.PrivateKeyPolicy{
   126  				keys.PrivateKeyPolicyNone,
   127  				keys.PrivateKeyPolicyHardwareKey,
   128  				keys.PrivateKeyPolicyHardwareKeyTouch,
   129  			},
   130  			wantPolicy: keys.PrivateKeyPolicyHardwareKeyTouch,
   131  		}, {
   132  			name: "pin policy",
   133  			policySet: []keys.PrivateKeyPolicy{
   134  				keys.PrivateKeyPolicyNone,
   135  				keys.PrivateKeyPolicyHardwareKey,
   136  				keys.PrivateKeyPolicyHardwareKeyPIN,
   137  			},
   138  			wantPolicy: keys.PrivateKeyPolicyHardwareKeyPIN,
   139  		}, {
   140  			name: "touch policy and pin policy",
   141  			policySet: []keys.PrivateKeyPolicy{
   142  				keys.PrivateKeyPolicyNone,
   143  				keys.PrivateKeyPolicyHardwareKey,
   144  				keys.PrivateKeyPolicyHardwareKeyPIN,
   145  				keys.PrivateKeyPolicyHardwareKeyTouch,
   146  			},
   147  			wantPolicy: keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
   148  		}, {
   149  			name: "touch and pin policy",
   150  			policySet: []keys.PrivateKeyPolicy{
   151  				keys.PrivateKeyPolicyNone,
   152  				keys.PrivateKeyPolicyHardwareKey,
   153  				keys.PrivateKeyPolicyHardwareKeyTouch,
   154  				keys.PrivateKeyPolicyHardwareKeyPIN,
   155  				keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
   156  			},
   157  			wantPolicy: keys.PrivateKeyPolicyHardwareKeyTouchAndPIN,
   158  		},
   159  	}
   160  	for _, tc := range testCases {
   161  		t.Run(tc.name, func(t *testing.T) {
   162  			requiredPolicy, err := keys.PolicyThatSatisfiesSet(tc.policySet)
   163  			require.NoError(t, err)
   164  			require.Equal(t, tc.wantPolicy, requiredPolicy)
   165  
   166  			// reversing the policy set shouldn't change the output
   167  			slices.Reverse(tc.policySet)
   168  
   169  			requiredPolicy, err = keys.PolicyThatSatisfiesSet(tc.policySet)
   170  			require.NoError(t, err)
   171  			require.Equal(t, tc.wantPolicy, requiredPolicy)
   172  		})
   173  	}
   174  }
   175  
   176  // TestParsePrivateKeyPolicyError tests private key policy error parsing and checking.
   177  func TestParsePrivateKeyPolicyError(t *testing.T) {
   178  	type testCase struct {
   179  		desc                    string
   180  		errIn                   error
   181  		expectIsKeyPolicy       bool
   182  		expectParseKeyPolicyErr bool
   183  		expectKeyPolicy         keys.PrivateKeyPolicy
   184  	}
   185  
   186  	testCases := []testCase{
   187  		{
   188  			desc:                    "random error",
   189  			errIn:                   trace.BadParameter("random error"),
   190  			expectIsKeyPolicy:       false,
   191  			expectParseKeyPolicyErr: true,
   192  		}, {
   193  			desc:                    "unknown_key_policy",
   194  			errIn:                   keys.NewPrivateKeyPolicyError("unknown_key_policy"),
   195  			expectIsKeyPolicy:       true,
   196  			expectParseKeyPolicyErr: true,
   197  		}, {
   198  			desc:              "wrapped policy error",
   199  			errIn:             trace.Wrap(keys.NewPrivateKeyPolicyError(keys.PrivateKeyPolicyHardwareKeyTouch), "wrapped err"),
   200  			expectIsKeyPolicy: true,
   201  			expectKeyPolicy:   keys.PrivateKeyPolicyHardwareKeyTouch,
   202  		}, {
   203  			desc:              "policy error string contained in error",
   204  			errIn:             trace.Errorf("ssh: rejected: administratively prohibited (%s)", keys.NewPrivateKeyPolicyError(keys.PrivateKeyPolicyHardwareKeyTouch).Error()),
   205  			expectIsKeyPolicy: true,
   206  			expectKeyPolicy:   keys.PrivateKeyPolicyHardwareKeyTouch,
   207  		},
   208  	}
   209  
   210  	for _, policy := range privateKeyPolicies {
   211  		testCases = append(testCases, testCase{
   212  			desc:              fmt.Sprintf("valid key policy: %v", policy),
   213  			errIn:             keys.NewPrivateKeyPolicyError(policy),
   214  			expectIsKeyPolicy: true,
   215  			expectKeyPolicy:   policy,
   216  		})
   217  	}
   218  
   219  	for _, tc := range testCases {
   220  		t.Run(tc.desc, func(t *testing.T) {
   221  			require.Equal(t, tc.expectIsKeyPolicy, keys.IsPrivateKeyPolicyError(tc.errIn))
   222  
   223  			keyPolicy, err := keys.ParsePrivateKeyPolicyError(tc.errIn)
   224  			if tc.expectParseKeyPolicyErr {
   225  				require.Error(t, err)
   226  			} else {
   227  				require.NoError(t, err)
   228  				require.Equal(t, tc.expectKeyPolicy, keyPolicy)
   229  			}
   230  		})
   231  	}
   232  }