github.com/greenpau/go-authcrunch@v1.1.4/pkg/kms/keystore_test.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 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 package kms 16 17 import ( 18 "fmt" 19 jwtlib "github.com/golang-jwt/jwt/v4" 20 "github.com/greenpau/go-authcrunch/internal/tests" 21 "github.com/greenpau/go-authcrunch/pkg/errors" 22 "github.com/greenpau/go-authcrunch/pkg/requests" 23 "github.com/greenpau/go-authcrunch/pkg/user" 24 "testing" 25 "time" 26 ) 27 28 type TestUserClaims struct { 29 Roles []string `json:"roles,omitempty" xml:"roles" yaml:"roles,omitempty"` 30 Role string `json:"role,omitempty" xml:"role" yaml:"role,omitempty"` 31 Groups []string `json:"groups,omitempty" xml:"groups" yaml:"groups,omitempty"` 32 Group string `json:"group,omitempty" xml:"group" yaml:"group,omitempty"` 33 Organizations []string `json:"org,omitempty" xml:"org" yaml:"org,omitempty"` 34 Address string `json:"addr,omitempty" xml:"addr" yaml:"addr,omitempty"` 35 AppMetadata map[string]interface{} `json:"app_metadata,omitempty" xml:"app_metadata" yaml:"app_metadata,omitempty"` 36 jwtlib.StandardClaims 37 } 38 39 func TestKeystoreOperators(t *testing.T) { 40 testcases := []struct { 41 name string 42 config string 43 signTokenName string 44 signAlgorithm string 45 verifyTokenName string 46 sign bool 47 user *user.User 48 claims *TestUserClaims 49 roles []string 50 addr string 51 operatorErr bool 52 operatorSignErr bool 53 err error 54 shouldErr bool 55 }{ 56 { 57 name: "user with roles claims and ip address", 58 config: `crypto key sign-verify foobar`, 59 claims: &TestUserClaims{ 60 Roles: []string{"admin", "editor", "viewer"}, 61 StandardClaims: jwtlib.StandardClaims{ 62 ExpiresAt: time.Now().Add(10 * time.Minute).Unix(), 63 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 64 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 65 Subject: "smithj@outlook.com", 66 }, 67 }, 68 roles: []string{"admin", "editor", "viewer"}, 69 addr: "127.0.0.1", 70 }, 71 { 72 name: "user with groups claims and ip address", 73 config: `crypto key sign-verify foobar`, 74 claims: &TestUserClaims{ 75 Groups: []string{"admin", "editor", "viewer"}, 76 StandardClaims: jwtlib.StandardClaims{ 77 ExpiresAt: time.Now().Add(10 * time.Minute).Unix(), 78 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 79 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 80 Subject: "smithj@outlook.com", 81 }, 82 }, 83 roles: []string{"admin", "editor", "viewer"}, 84 addr: "127.0.0.1", 85 }, 86 { 87 name: "user with role claim and ip address", 88 config: `crypto key sign-verify foobar`, 89 claims: &TestUserClaims{ 90 Role: "admin", 91 Address: "192.168.1.1", 92 StandardClaims: jwtlib.StandardClaims{ 93 ExpiresAt: time.Now().Add(10 * time.Minute).Unix(), 94 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 95 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 96 Subject: "smithj@outlook.com", 97 }, 98 }, 99 roles: []string{"admin"}, 100 addr: "192.168.1.1", 101 }, 102 { 103 name: "user with group claim and ip address", 104 config: `crypto key sign-verify foobar`, 105 claims: &TestUserClaims{ 106 Group: "admin", 107 Address: "192.168.1.1", 108 StandardClaims: jwtlib.StandardClaims{ 109 ExpiresAt: time.Now().Add(10 * time.Minute).Unix(), 110 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 111 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 112 Subject: "smithj@outlook.com", 113 }, 114 }, 115 roles: []string{"admin"}, 116 addr: "192.168.1.1", 117 }, 118 { 119 name: "user with expired token", 120 config: `crypto key sign-verify foobar`, 121 claims: &TestUserClaims{ 122 Roles: []string{"admin", "editor", "viewer"}, 123 StandardClaims: jwtlib.StandardClaims{ 124 ExpiresAt: time.Now().Add(5 * time.Minute * -1).Unix(), 125 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 126 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 127 Subject: "smithj@outlook.com", 128 }, 129 }, 130 roles: []string{"admin", "editor", "viewer"}, 131 addr: "127.0.0.1", 132 shouldErr: true, 133 err: errors.ErrCryptoKeyStoreParseTokenExpired, 134 }, 135 { 136 name: "user with not yet ready token", 137 config: `crypto key sign-verify foobar`, 138 claims: &TestUserClaims{ 139 Roles: []string{"admin", "editor", "viewer"}, 140 AppMetadata: map[string]interface{}{ 141 "authorization": map[string]interface{}{ 142 "roles": []interface{}{ 143 1, 2, 3, 144 }, 145 }, 146 }, 147 StandardClaims: jwtlib.StandardClaims{ 148 ExpiresAt: time.Now().Add(20 * time.Minute).Unix(), 149 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 150 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 151 Subject: "smithj@outlook.com", 152 }, 153 }, 154 roles: []string{"admin", "editor", "viewer"}, 155 addr: "127.0.0.1", 156 shouldErr: true, 157 err: errors.ErrCryptoKeyStoreTokenData, 158 }, 159 { 160 name: "nil keys", 161 shouldErr: true, 162 err: errors.ErrCryptoKeyStoreAddKeyNil, 163 }, 164 { 165 name: "token name mismatch", 166 config: `crypto key sign-verify foobar`, 167 verifyTokenName: `foobar`, 168 claims: &TestUserClaims{ 169 Group: "admin", 170 Address: "192.168.1.1", 171 StandardClaims: jwtlib.StandardClaims{ 172 ExpiresAt: time.Now().Add(10 * time.Minute).Unix(), 173 IssuedAt: time.Now().Add(10 * time.Minute * -1).Unix(), 174 NotBefore: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(), 175 Subject: "smithj@outlook.com", 176 }, 177 }, 178 roles: []string{"admin"}, 179 addr: "192.168.1.1", 180 shouldErr: true, 181 err: errors.ErrCryptoKeyStoreParseTokenFailed, 182 }, 183 { 184 name: "failed verification", 185 config: `crypto key sign-verify foobar`, 186 sign: true, 187 signAlgorithm: "HS512", 188 verifyTokenName: `foobar`, 189 user: newTestUser(), 190 operatorErr: true, 191 shouldErr: true, 192 err: errors.ErrCryptoKeyStoreParseTokenFailed, 193 }, 194 { 195 name: "failed signing due to algo mismatch", 196 config: `crypto key sign-verify foobar`, 197 sign: true, 198 signAlgorithm: "RS512", 199 verifyTokenName: `foobar`, 200 user: newTestUser(), 201 operatorErr: true, 202 operatorSignErr: true, 203 shouldErr: true, 204 err: errors.ErrUnsupportedSigningMethod.WithArgs("RS512"), 205 }, 206 { 207 name: "failed signing due to token name mismatch", 208 config: `crypto key sign-verify foobar`, 209 sign: true, 210 signAlgorithm: "RS512", 211 signTokenName: `foobar`, 212 user: newTestUser(), 213 operatorErr: true, 214 operatorSignErr: true, 215 shouldErr: true, 216 err: errors.ErrCryptoKeyStoreSignTokenFailed, 217 }, 218 } 219 for _, tc := range testcases { 220 t.Run(tc.name, func(t *testing.T) { 221 var signedToken string 222 var msgs []string 223 var keys []*CryptoKey 224 msgs = append(msgs, fmt.Sprintf("test name: %s", tc.name)) 225 if tc.config != "" { 226 configs, err := ParseCryptoKeyConfigs(tc.config) 227 if err != nil { 228 t.Fatalf("failed parsing configs: %v", err) 229 } 230 keys, err = GetKeysFromConfigs(configs) 231 if err != nil { 232 t.Fatalf("failed getting keys from configs: %v", err) 233 } 234 } else { 235 keys = []*CryptoKey{nil} 236 } 237 238 ks := NewCryptoKeyStore() 239 if err := ks.AddKeys(keys); err != nil { 240 if !tc.operatorErr { 241 if tests.EvalErrWithLog(t, err, "add keys", tc.shouldErr, tc.err, msgs) { 242 return 243 } 244 t.Fatalf("failed adding keys to crypto key store: %v", err) 245 } 246 } 247 248 privKey := keys[0] 249 // pubKey := keys[0] 250 // if err := ks.SignToken(privKey.Sign.Token.Name, privKey.Sign.Token.DefaultMethod, usr); err != nil { 251 // t.Fatal(err) 252 // } 253 if tc.signTokenName == "" { 254 tc.signTokenName = privKey.Sign.Token.Name 255 } 256 257 if tc.sign { 258 err := ks.SignToken(tc.signTokenName, tc.signAlgorithm, tc.user) 259 if tc.operatorSignErr { 260 if tests.EvalErrWithLog(t, err, "sign token", tc.shouldErr, tc.err, msgs) { 261 return 262 } 263 } 264 } else { 265 token := jwtlib.NewWithClaims(jwtlib.SigningMethodHS512, tc.claims) 266 var err error 267 signedToken, err = token.SignedString(privKey.Sign.Secret) 268 if err != nil { 269 t.Fatalf("failed signing claims: %s", err) 270 } 271 } 272 273 msgs = append(msgs, fmt.Sprintf("signed token: %s", signedToken)) 274 275 if tc.verifyTokenName == "" { 276 tc.verifyTokenName = privKey.Sign.Token.Name 277 } 278 279 ar := requests.NewAuthorizationRequest() 280 ar.ID = "TEST_REQUEST_ID" 281 ar.SessionID = "TEST_SESSION_ID" 282 ar.Token.Name = tc.verifyTokenName 283 ar.Token.Payload = signedToken 284 usr, err := ks.ParseToken(ar) 285 if tests.EvalErrWithLog(t, err, "parse token", tc.shouldErr, tc.err, msgs) { 286 return 287 } 288 289 msgs = append(msgs, fmt.Sprintf("parsed claims: %v", usr.Claims)) 290 msgs = append(msgs, fmt.Sprintf("roles: %v", usr.Claims.Roles)) 291 if len(tc.roles) > 0 { 292 tests.EvalObjectsWithLog(t, "roles", tc.roles, usr.Claims.Roles, msgs) 293 } 294 }) 295 } 296 } 297 298 func TestCryptoKeyStoreAutoGenerate(t *testing.T) { 299 var testcases = []struct { 300 name string 301 tag string 302 algorithm string 303 shouldErr bool 304 err error 305 }{ 306 { 307 name: "generate es512 key pair", 308 tag: "default", 309 algorithm: "ES512", 310 // shouldErr: true, 311 //err: fmt.Errorf(`kms: file "foo" is not supported due to extension type`), 312 }, 313 } 314 for _, tc := range testcases { 315 t.Run(tc.name, func(t *testing.T) { 316 msgs := []string{fmt.Sprintf("test name: %s", tc.name)} 317 msgs = append(msgs, fmt.Sprintf("algorithm: %s", tc.algorithm)) 318 ks := NewCryptoKeyStore() 319 err := ks.AutoGenerate(tc.tag, tc.algorithm) 320 if tests.EvalErrWithLog(t, err, nil, tc.shouldErr, tc.err, msgs) { 321 return 322 } 323 }) 324 } 325 }