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