github.com/simranvc/fabric-ca@v0.0.0-20191030094829-acc364294dde/util/util.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 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 util 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "crypto/rsa" 23 "crypto/x509" 24 "encoding/base64" 25 "encoding/json" 26 "encoding/pem" 27 "fmt" 28 "io" 29 "io/ioutil" 30 "math/big" 31 mrand "math/rand" 32 "net/http" 33 "net/url" 34 "os" 35 "path" 36 "path/filepath" 37 "reflect" 38 "regexp" 39 "strings" 40 "testing" 41 "time" 42 43 "github.com/cloudflare/cfssl/log" 44 "github.com/hyperledger/fabric/bccsp" 45 "github.com/pkg/errors" 46 "github.com/spf13/viper" 47 "github.com/stretchr/testify/assert" 48 "golang.org/x/crypto/ocsp" 49 ) 50 51 var ( 52 rnd = mrand.NewSource(time.Now().UnixNano()) 53 // ErrNotImplemented used to return errors for functions not implemented 54 ErrNotImplemented = errors.New("NOT YET IMPLEMENTED") 55 ) 56 57 const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 58 const ( 59 letterIdxBits = 6 // 6 bits to represent a letter index 60 letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits 61 letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits 62 ) 63 64 // RevocationReasonCodes is a map between string reason codes to integers as defined in RFC 5280 65 var RevocationReasonCodes = map[string]int{ 66 "unspecified": ocsp.Unspecified, 67 "keycompromise": ocsp.KeyCompromise, 68 "cacompromise": ocsp.CACompromise, 69 "affiliationchanged": ocsp.AffiliationChanged, 70 "superseded": ocsp.Superseded, 71 "cessationofoperation": ocsp.CessationOfOperation, 72 "certificatehold": ocsp.CertificateHold, 73 "removefromcrl": ocsp.RemoveFromCRL, 74 "privilegewithdrawn": ocsp.PrivilegeWithdrawn, 75 "aacompromise": ocsp.AACompromise, 76 } 77 78 // SecretTag to tag a field as secret as in password, token 79 const SecretTag = "mask" 80 81 // URLRegex is the regular expression to check if a value is an URL 82 var URLRegex = regexp.MustCompile("(ldap|http)s*://(\\S+):(\\S+)@") 83 84 //ECDSASignature forms the structure for R and S value for ECDSA 85 type ECDSASignature struct { 86 R, S *big.Int 87 } 88 89 // RandomString returns a random string 90 func RandomString(n int) string { 91 b := make([]byte, n) 92 93 for i, cache, remain := n-1, rnd.Int63(), letterIdxMax; i >= 0; { 94 if remain == 0 { 95 cache, remain = rnd.Int63(), letterIdxMax 96 } 97 if idx := int(cache & letterIdxMask); idx < len(letterBytes) { 98 b[i] = letterBytes[idx] 99 i-- 100 } 101 cache >>= letterIdxBits 102 remain-- 103 } 104 105 return string(b) 106 } 107 108 // RemoveQuotes removes outer quotes from a string if necessary 109 func RemoveQuotes(str string) string { 110 if str == "" { 111 return str 112 } 113 if (strings.HasPrefix(str, "'") && strings.HasSuffix(str, "'")) || 114 (strings.HasPrefix(str, "\"") && strings.HasSuffix(str, "\"")) { 115 str = str[1 : len(str)-1] 116 } 117 return str 118 } 119 120 // ReadFile reads a file 121 func ReadFile(file string) ([]byte, error) { 122 return ioutil.ReadFile(file) 123 } 124 125 // WriteFile writes a file 126 func WriteFile(file string, buf []byte, perm os.FileMode) error { 127 dir := path.Dir(file) 128 // Create the directory if it doesn't exist 129 if _, err := os.Stat(dir); os.IsNotExist(err) { 130 err = os.MkdirAll(dir, 0755) 131 if err != nil { 132 return errors.Wrapf(err, "Failed to create directory '%s' for file '%s'", dir, file) 133 } 134 } 135 return ioutil.WriteFile(file, buf, perm) 136 } 137 138 // FileExists checks to see if a file exists 139 func FileExists(name string) bool { 140 if _, err := os.Stat(name); err != nil { 141 if os.IsNotExist(err) { 142 return false 143 } 144 } 145 return true 146 } 147 148 // Marshal to bytes 149 func Marshal(from interface{}, what string) ([]byte, error) { 150 buf, err := json.Marshal(from) 151 if err != nil { 152 return nil, errors.Wrapf(err, "Failed to marshal %s", what) 153 } 154 return buf, nil 155 } 156 157 // Unmarshal from bytes 158 func Unmarshal(from []byte, to interface{}, what string) error { 159 err := json.Unmarshal(from, to) 160 if err != nil { 161 return errors.Wrapf(err, "Failed to unmarshal %s", what) 162 } 163 return nil 164 } 165 166 // CreateToken creates a JWT-like token. 167 // In a normal JWT token, the format of the token created is: 168 // <algorithm,claims,signature> 169 // where each part is base64-encoded string separated by a period. 170 // In this JWT-like token, there are two differences: 171 // 1) the claims section is a certificate, so the format is: 172 // <certificate,signature> 173 // 2) the signature uses the private key associated with the certificate, 174 // and the signature is across both the certificate and the "body" argument, 175 // which is the body of an HTTP request, though could be any arbitrary bytes. 176 // @param cert The pem-encoded certificate 177 // @param key The pem-encoded key 178 // @param method http method of the request 179 // @param uri URI of the request 180 // @param body The body of an HTTP request 181 func CreateToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) { 182 x509Cert, err := GetX509CertificateFromPEM(cert) 183 if err != nil { 184 return "", err 185 } 186 publicKey := x509Cert.PublicKey 187 188 var token string 189 190 //The RSA Key Gen is commented right now as there is bccsp does 191 switch publicKey.(type) { 192 /* 193 case *rsa.PublicKey: 194 token, err = GenRSAToken(csp, cert, key, body) 195 if err != nil { 196 return "", err 197 } 198 */ 199 case *ecdsa.PublicKey: 200 token, err = GenECDSAToken(csp, cert, key, method, uri, body) 201 if err != nil { 202 return "", err 203 } 204 } 205 return token, nil 206 } 207 208 //GenRSAToken signs the http body and cert with RSA using RSA private key 209 // @csp : BCCSP instance 210 /* 211 func GenRSAToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) { 212 privKey, err := GetRSAPrivateKey(key) 213 if err != nil { 214 return "", err 215 } 216 b64body := B64Encode(body) 217 b64cert := B64Encode(cert) 218 bodyAndcert := b64body + "." + b64cert 219 hash := sha512.New384() 220 hash.Write([]byte(bodyAndcert)) 221 h := hash.Sum(nil) 222 RSAsignature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA384, h[:]) 223 if err != nil { 224 return "", errors.Wrap(err, "Failed to rsa.SignPKCS1v15") 225 } 226 b64sig := B64Encode(RSAsignature) 227 token := b64cert + "." + b64sig 228 229 return token, nil 230 } 231 */ 232 233 //GenECDSAToken signs the http body and cert with ECDSA using EC private key 234 func GenECDSAToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) { 235 b64body := B64Encode(body) 236 b64cert := B64Encode(cert) 237 b64uri := B64Encode([]byte(uri)) 238 payload := method + "." + b64uri + "." + b64body + "." + b64cert 239 240 return genECDSAToken(csp, key, b64cert, payload) 241 } 242 243 func genECDSAToken(csp bccsp.BCCSP, key bccsp.Key, b64cert, payload string) (string, error) { 244 digest, digestError := csp.Hash([]byte(payload), &bccsp.SHAOpts{}) 245 if digestError != nil { 246 return "", errors.WithMessage(digestError, fmt.Sprintf("Hash failed on '%s'", payload)) 247 } 248 249 ecSignature, err := csp.Sign(key, digest, nil) 250 if err != nil { 251 return "", errors.WithMessage(err, "BCCSP signature generation failure") 252 } 253 if len(ecSignature) == 0 { 254 return "", errors.New("BCCSP signature creation failed. Signature must be different than nil") 255 } 256 257 b64sig := B64Encode(ecSignature) 258 token := b64cert + "." + b64sig 259 260 return token, nil 261 262 } 263 264 // VerifyToken verifies token signed by either ECDSA or RSA and 265 // returns the associated user ID 266 func VerifyToken(csp bccsp.BCCSP, token string, method, uri string, body []byte, compMode1_3 bool) (*x509.Certificate, error) { 267 268 if csp == nil { 269 return nil, errors.New("BCCSP instance is not present") 270 } 271 x509Cert, b64Cert, b64Sig, err := DecodeToken(token) 272 if err != nil { 273 return nil, err 274 } 275 sig, err := B64Decode(b64Sig) 276 if err != nil { 277 return nil, errors.WithMessage(err, "Invalid base64 encoded signature in token") 278 } 279 b64Body := B64Encode(body) 280 b64uri := B64Encode([]byte(uri)) 281 sigString := method + "." + b64uri + "." + b64Body + "." + b64Cert 282 283 pk2, err := csp.KeyImport(x509Cert, &bccsp.X509PublicKeyImportOpts{Temporary: true}) 284 if err != nil { 285 return nil, errors.WithMessage(err, "Public Key import into BCCSP failed with error") 286 } 287 if pk2 == nil { 288 return nil, errors.New("Public Key Cannot be imported into BCCSP") 289 } 290 291 //bccsp.X509PublicKeyImportOpts 292 //Using default hash algo 293 digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{}) 294 if digestError != nil { 295 return nil, errors.WithMessage(digestError, "Message digest failed") 296 } 297 298 valid, validErr := csp.Verify(pk2, sig, digest, nil) 299 if compMode1_3 && !valid { 300 log.Debugf("Failed to verify token based on new authentication header requirements: %s", err) 301 sigString := b64Body + "." + b64Cert 302 digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{}) 303 if digestError != nil { 304 return nil, errors.WithMessage(digestError, "Message digest failed") 305 } 306 valid, validErr = csp.Verify(pk2, sig, digest, nil) 307 } 308 309 if validErr != nil { 310 return nil, errors.WithMessage(validErr, "Token signature validation failure") 311 } 312 if !valid { 313 return nil, errors.New("Token signature validation failed") 314 } 315 316 return x509Cert, nil 317 } 318 319 // DecodeToken extracts an X509 certificate and base64 encoded signature from a token 320 func DecodeToken(token string) (*x509.Certificate, string, string, error) { 321 if token == "" { 322 return nil, "", "", errors.New("Invalid token; it is empty") 323 } 324 parts := strings.Split(token, ".") 325 if len(parts) != 2 { 326 return nil, "", "", errors.New("Invalid token format; expecting 2 parts separated by '.'") 327 } 328 b64cert := parts[0] 329 certDecoded, err := B64Decode(b64cert) 330 if err != nil { 331 return nil, "", "", errors.WithMessage(err, "Failed to decode base64 encoded x509 cert") 332 } 333 x509Cert, err := GetX509CertificateFromPEM(certDecoded) 334 if err != nil { 335 return nil, "", "", errors.WithMessage(err, "Error in parsing x509 certificate given block bytes") 336 } 337 return x509Cert, b64cert, parts[1], nil 338 } 339 340 //GetECPrivateKey get *ecdsa.PrivateKey from key pem 341 func GetECPrivateKey(raw []byte) (*ecdsa.PrivateKey, error) { 342 decoded, _ := pem.Decode(raw) 343 if decoded == nil { 344 return nil, errors.New("Failed to decode the PEM-encoded ECDSA key") 345 } 346 ECprivKey, err := x509.ParseECPrivateKey(decoded.Bytes) 347 if err == nil { 348 return ECprivKey, nil 349 } 350 key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes) 351 if err2 == nil { 352 switch key.(type) { 353 case *ecdsa.PrivateKey: 354 return key.(*ecdsa.PrivateKey), nil 355 case *rsa.PrivateKey: 356 return nil, errors.New("Expecting EC private key but found RSA private key") 357 default: 358 return nil, errors.New("Invalid private key type in PKCS#8 wrapping") 359 } 360 } 361 return nil, errors.Wrap(err2, "Failed parsing EC private key") 362 } 363 364 //GetRSAPrivateKey get *rsa.PrivateKey from key pem 365 func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) { 366 decoded, _ := pem.Decode(raw) 367 if decoded == nil { 368 return nil, errors.New("Failed to decode the PEM-encoded RSA key") 369 } 370 RSAprivKey, err := x509.ParsePKCS1PrivateKey(decoded.Bytes) 371 if err == nil { 372 return RSAprivKey, nil 373 } 374 key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes) 375 if err2 == nil { 376 switch key.(type) { 377 case *ecdsa.PrivateKey: 378 return nil, errors.New("Expecting RSA private key but found EC private key") 379 case *rsa.PrivateKey: 380 return key.(*rsa.PrivateKey), nil 381 default: 382 return nil, errors.New("Invalid private key type in PKCS#8 wrapping") 383 } 384 } 385 return nil, errors.Wrap(err, "Failed parsing RSA private key") 386 } 387 388 // B64Encode base64 encodes bytes 389 func B64Encode(buf []byte) string { 390 return base64.StdEncoding.EncodeToString(buf) 391 } 392 393 // B64Decode base64 decodes a string 394 func B64Decode(str string) (buf []byte, err error) { 395 return base64.StdEncoding.DecodeString(str) 396 } 397 398 // StrContained returns true if 'str' is in 'strs'; otherwise return false 399 func StrContained(str string, strs []string) bool { 400 for _, s := range strs { 401 if strings.ToLower(s) == strings.ToLower(str) { 402 return true 403 } 404 } 405 return false 406 } 407 408 // IsSubsetOf returns an error if there is something in 'small' that 409 // is not in 'big'. Both small and big are assumed to be comma-separated 410 // strings. All string comparisons are case-insensitive. 411 // Examples: 412 // 1) IsSubsetOf('a,B', 'A,B,C') returns nil 413 // 2) IsSubsetOf('A,B,C', 'B,C') returns an error because A is not in the 2nd set. 414 func IsSubsetOf(small, big string) error { 415 bigSet := strings.Split(big, ",") 416 smallSet := strings.Split(small, ",") 417 for _, s := range smallSet { 418 if s != "" && !StrContained(s, bigSet) { 419 return errors.Errorf("'%s' is not a member of '%s'", s, big) 420 } 421 } 422 return nil 423 } 424 425 // HTTPRequestToString returns a string for an HTTP request for debuggging 426 func HTTPRequestToString(req *http.Request) string { 427 body, _ := ioutil.ReadAll(req.Body) 428 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 429 return fmt.Sprintf("%s %s\n%s", 430 req.Method, req.URL, string(body)) 431 } 432 433 // HTTPResponseToString returns a string for an HTTP response for debuggging 434 func HTTPResponseToString(resp *http.Response) string { 435 body, _ := ioutil.ReadAll(resp.Body) 436 resp.Body = ioutil.NopCloser(bytes.NewReader(body)) 437 return fmt.Sprintf("statusCode=%d (%s)\n%s", 438 resp.StatusCode, resp.Status, string(body)) 439 } 440 441 // CreateClientHome will create a home directory if it does not exist 442 func CreateClientHome() (string, error) { 443 log.Debug("CreateHome") 444 home := filepath.Dir(GetDefaultConfigFile("fabric-ca-client")) 445 446 if _, err := os.Stat(home); err != nil { 447 if os.IsNotExist(err) { 448 err := os.MkdirAll(home, 0755) 449 if err != nil { 450 return "", err 451 } 452 } 453 } 454 return home, nil 455 } 456 457 // GetDefaultConfigFile gets the default path for the config file to display in usage message 458 func GetDefaultConfigFile(cmdName string) string { 459 if cmdName == "fabric-ca-server" { 460 var fname = fmt.Sprintf("%s-config.yaml", cmdName) 461 // First check home env variables 462 home := "." 463 envs := []string{"FABRIC_CA_SERVER_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"} 464 for _, env := range envs { 465 envVal := os.Getenv(env) 466 if envVal != "" { 467 home = envVal 468 break 469 } 470 } 471 return path.Join(home, fname) 472 } 473 474 var fname = fmt.Sprintf("%s-config.yaml", cmdName) 475 // First check home env variables 476 var home string 477 envs := []string{"FABRIC_CA_CLIENT_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"} 478 for _, env := range envs { 479 envVal := os.Getenv(env) 480 if envVal != "" { 481 home = envVal 482 return path.Join(home, fname) 483 } 484 } 485 486 return path.Join(os.Getenv("HOME"), ".fabric-ca-client", fname) 487 } 488 489 // GetX509CertificateFromPEMFile gets an X509 certificate from a file 490 func GetX509CertificateFromPEMFile(file string) (*x509.Certificate, error) { 491 pemBytes, err := ReadFile(file) 492 if err != nil { 493 return nil, err 494 } 495 x509Cert, err := GetX509CertificateFromPEM(pemBytes) 496 if err != nil { 497 return nil, errors.Wrapf(err, "Invalid certificate in '%s'", file) 498 } 499 return x509Cert, nil 500 } 501 502 // GetX509CertificateFromPEM get an X509 certificate from bytes in PEM format 503 func GetX509CertificateFromPEM(cert []byte) (*x509.Certificate, error) { 504 block, _ := pem.Decode(cert) 505 if block == nil { 506 return nil, errors.New("Failed to PEM decode certificate") 507 } 508 x509Cert, err := x509.ParseCertificate(block.Bytes) 509 if err != nil { 510 return nil, errors.Wrap(err, "Error parsing certificate") 511 } 512 return x509Cert, nil 513 } 514 515 // GetX509CertificatesFromPEM returns X509 certificates from bytes in PEM format 516 func GetX509CertificatesFromPEM(pemBytes []byte) ([]*x509.Certificate, error) { 517 chain := pemBytes 518 var certs []*x509.Certificate 519 for len(chain) > 0 { 520 var block *pem.Block 521 block, chain = pem.Decode(chain) 522 if block == nil { 523 break 524 } 525 526 cert, err := x509.ParseCertificate(block.Bytes) 527 if err != nil { 528 return nil, errors.Wrap(err, "Error parsing certificate") 529 } 530 certs = append(certs, cert) 531 } 532 return certs, nil 533 } 534 535 // GetCertificateDurationFromFile returns the validity duration for a certificate 536 // in a file. 537 func GetCertificateDurationFromFile(file string) (time.Duration, error) { 538 cert, err := GetX509CertificateFromPEMFile(file) 539 if err != nil { 540 return 0, err 541 } 542 return GetCertificateDuration(cert), nil 543 } 544 545 // GetCertificateDuration returns the validity duration for a certificate 546 func GetCertificateDuration(cert *x509.Certificate) time.Duration { 547 return cert.NotAfter.Sub(cert.NotBefore) 548 } 549 550 // GetEnrollmentIDFromPEM returns the EnrollmentID from a PEM buffer 551 func GetEnrollmentIDFromPEM(cert []byte) (string, error) { 552 x509Cert, err := GetX509CertificateFromPEM(cert) 553 if err != nil { 554 return "", err 555 } 556 return GetEnrollmentIDFromX509Certificate(x509Cert), nil 557 } 558 559 // GetEnrollmentIDFromX509Certificate returns the EnrollmentID from the X509 certificate 560 func GetEnrollmentIDFromX509Certificate(cert *x509.Certificate) string { 561 return cert.Subject.CommonName 562 } 563 564 // MakeFileAbs makes 'file' absolute relative to 'dir' if not already absolute 565 func MakeFileAbs(file, dir string) (string, error) { 566 if file == "" { 567 return "", nil 568 } 569 if filepath.IsAbs(file) { 570 return file, nil 571 } 572 path, err := filepath.Abs(filepath.Join(dir, file)) 573 if err != nil { 574 return "", errors.Wrapf(err, "Failed making '%s' absolute based on '%s'", file, dir) 575 } 576 return path, nil 577 } 578 579 // MakeFileNamesAbsolute makes all file names in the list absolute, relative to home 580 func MakeFileNamesAbsolute(files []*string, home string) error { 581 for _, filePtr := range files { 582 abs, err := MakeFileAbs(*filePtr, home) 583 if err != nil { 584 return err 585 } 586 *filePtr = abs 587 } 588 return nil 589 } 590 591 // Fatal logs a fatal message and exits 592 func Fatal(format string, v ...interface{}) { 593 log.Fatalf(format, v...) 594 os.Exit(1) 595 } 596 597 // GetUser returns username and password from CLI input 598 func GetUser(v *viper.Viper) (string, string, error) { 599 var fabricCAServerURL string 600 fabricCAServerURL = v.GetString("url") 601 602 URL, err := url.Parse(fabricCAServerURL) 603 if err != nil { 604 return "", "", err 605 } 606 607 user := URL.User 608 if user == nil { 609 return "", "", errors.New("No username and password provided as part of the Fabric CA server URL") 610 } 611 612 eid := user.Username() 613 if eid == "" { 614 return "", "", errors.New("No username provided as part of URL") 615 } 616 617 pass, _ := user.Password() 618 if pass == "" { 619 return "", "", errors.New("No password provided as part of URL") 620 } 621 622 return eid, pass, nil 623 } 624 625 // GetSerialAsHex returns the serial number from certificate as hex format 626 func GetSerialAsHex(serial *big.Int) string { 627 hex := fmt.Sprintf("%x", serial) 628 return hex 629 } 630 631 // StructToString converts a struct to a string. If a field 632 // has a 'secret' tag, it is masked in the returned string 633 func StructToString(si interface{}) string { 634 rval := reflect.ValueOf(si).Elem() 635 tipe := rval.Type() 636 var buffer bytes.Buffer 637 buffer.WriteString("{ ") 638 for i := 0; i < rval.NumField(); i++ { 639 tf := tipe.Field(i) 640 if !rval.FieldByName(tf.Name).CanSet() { 641 continue // skip unexported fields 642 } 643 var fStr string 644 tagv := tf.Tag.Get(SecretTag) 645 if tagv == "password" || tagv == "username" { 646 fStr = fmt.Sprintf("%s:**** ", tf.Name) 647 } else if tagv == "url" { 648 val, ok := rval.Field(i).Interface().(string) 649 if ok { 650 val = GetMaskedURL(val) 651 fStr = fmt.Sprintf("%s:%v ", tf.Name, val) 652 } else { 653 fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface()) 654 } 655 } else { 656 fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface()) 657 } 658 buffer.WriteString(fStr) 659 } 660 buffer.WriteString(" }") 661 return buffer.String() 662 } 663 664 // GetMaskedURL returns masked URL. It masks username and password from the URL 665 // if present 666 func GetMaskedURL(url string) string { 667 matches := URLRegex.FindStringSubmatch(url) 668 669 // If there is a match, there should be four entries: 1 for 670 // the match and 3 for submatches 671 if len(matches) == 4 { 672 matchIdxs := URLRegex.FindStringSubmatchIndex(url) 673 matchStr := url[matchIdxs[0]:matchIdxs[1]] 674 for idx := 2; idx < len(matches); idx++ { 675 if matches[idx] != "" { 676 matchStr = strings.Replace(matchStr, matches[idx], "****", 1) 677 } 678 } 679 url = url[:matchIdxs[0]] + matchStr + url[matchIdxs[1]:len(url)] 680 } 681 return url 682 } 683 684 // NormalizeStringSlice checks for seperators 685 func NormalizeStringSlice(slice []string) []string { 686 var normalizedSlice []string 687 688 for _, item := range slice { 689 // Remove surrounding brackets "[]" if specified 690 if strings.HasPrefix(item, "[") && strings.HasSuffix(item, "]") { 691 item = item[1 : len(item)-1] 692 } 693 // Split elements based on comma and add to normalized slice 694 elems := strings.Split(item, ",") 695 for _, elem := range elems { 696 normalizedSlice = append(normalizedSlice, strings.TrimSpace(elem)) 697 } 698 } 699 return normalizedSlice 700 } 701 702 // NormalizeFileList provides absolute pathing for the list of files 703 func NormalizeFileList(files []string, homeDir string) ([]string, error) { 704 var err error 705 706 files = NormalizeStringSlice(files) 707 708 for i, file := range files { 709 files[i], err = MakeFileAbs(file, homeDir) 710 if err != nil { 711 return nil, err 712 } 713 } 714 715 return files, nil 716 } 717 718 // CheckHostsInCert checks to see if host correctly inserted into certificate 719 func CheckHostsInCert(certFile string, hosts ...string) error { 720 certBytes, err := ioutil.ReadFile(certFile) 721 if err != nil { 722 return errors.Wrapf(err, "Failed to read certificate file at '%s'", certFile) 723 } 724 725 cert, err := GetX509CertificateFromPEM(certBytes) 726 if err != nil { 727 return errors.Wrap(err, "Failed to get certificate") 728 } 729 730 // combine DNSNames and IPAddresses from cert 731 sans := cert.DNSNames 732 for _, ip := range cert.IPAddresses { 733 sans = append(sans, ip.String()) 734 } 735 for _, host := range hosts { 736 if !containsString(sans, host) { 737 return errors.Errorf("Host '%s' was not found in the certificate in file '%s'", host, certFile) 738 } 739 } 740 return nil 741 } 742 743 func containsString(list []string, item string) bool { 744 for _, elem := range list { 745 if elem == item { 746 return true 747 } 748 } 749 return false 750 } 751 752 // Read reads from Reader into a byte array 753 func Read(r io.Reader, data []byte) ([]byte, error) { 754 j := 0 755 for { 756 n, err := r.Read(data[j:]) 757 j = j + n 758 if err != nil { 759 if err == io.EOF { 760 break 761 } 762 return nil, errors.Wrapf(err, "Read failure") 763 } 764 765 if (n == 0 && j == len(data)) || j > len(data) { 766 return nil, errors.New("Size of requested data is too large") 767 } 768 } 769 770 return data[:j], nil 771 } 772 773 // Hostname name returns the hostname of the machine 774 func Hostname() string { 775 hostname, _ := os.Hostname() 776 if hostname == "" { 777 hostname = "localhost" 778 } 779 return hostname 780 } 781 782 // ValidateAndReturnAbsConf checks to see that there are no conflicts between the 783 // configuration file path and home directory. If no conflicts, returns back the absolute 784 // path for the configuration file and home directory. 785 func ValidateAndReturnAbsConf(configFilePath, homeDir, cmdName string) (string, string, error) { 786 var err error 787 var homeDirSet bool 788 var configFileSet bool 789 790 defaultConfig := GetDefaultConfigFile(cmdName) // Get the default configuration 791 792 if configFilePath == "" { 793 configFilePath = defaultConfig // If no config file path specified, use the default configuration file 794 } else { 795 configFileSet = true 796 } 797 798 if homeDir == "" { 799 homeDir = filepath.Dir(defaultConfig) // If no home directory specified, use the default directory 800 } else { 801 homeDirSet = true 802 } 803 804 // Make the home directory absolute 805 homeDir, err = filepath.Abs(homeDir) 806 if err != nil { 807 return "", "", errors.Wrap(err, "Failed to get full path of config file") 808 } 809 homeDir = strings.TrimRight(homeDir, "/") 810 811 if configFileSet && homeDirSet { 812 log.Warning("Using both --config and --home CLI flags; --config will take precedence") 813 } 814 815 if configFileSet { 816 configFilePath, err = filepath.Abs(configFilePath) 817 if err != nil { 818 return "", "", errors.Wrap(err, "Failed to get full path of configuration file") 819 } 820 return configFilePath, filepath.Dir(configFilePath), nil 821 } 822 823 configFile := filepath.Join(homeDir, filepath.Base(defaultConfig)) // Join specified home directory with default config file name 824 return configFile, homeDir, nil 825 } 826 827 // GetSliceFromList will return a slice from a list 828 func GetSliceFromList(split string, delim string) []string { 829 return strings.Split(strings.Replace(split, " ", "", -1), delim) 830 } 831 832 // ListContains looks through a comma separated list to see if a string exists 833 func ListContains(list, find string) bool { 834 items := strings.Split(list, ",") 835 for _, item := range items { 836 item = strings.TrimPrefix(item, " ") 837 if item == find { 838 return true 839 } 840 } 841 return false 842 } 843 844 //TODO: move these out of production code 845 846 // FatalError will check to see if an error occured if so it will cause the test cases exit 847 func FatalError(t *testing.T, err error, msg string, args ...interface{}) { 848 if len(args) > 0 { 849 msg = fmt.Sprintf(msg, args) 850 } 851 if !assert.NoError(t, err, msg) { 852 t.Fatal(msg) 853 } 854 } 855 856 // ErrorContains will check to see if an error occurred, if so it will check that it contains 857 // the appropriate error message 858 func ErrorContains(t *testing.T, err error, contains, msg string, args ...interface{}) { 859 if len(args) > 0 { 860 msg = fmt.Sprintf(msg, args) 861 } 862 if assert.Error(t, err, msg) { 863 assert.Contains(t, err.Error(), contains) 864 } 865 }