github.com/minio/console@v1.4.1/api/admin_inspect.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2022 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "encoding/base64" 21 "fmt" 22 "io" 23 "net/http" 24 "strings" 25 "unicode/utf8" 26 27 "github.com/go-openapi/runtime" 28 "github.com/go-openapi/runtime/middleware" 29 "github.com/minio/console/api/operations" 30 inspectApi "github.com/minio/console/api/operations/inspect" 31 "github.com/minio/console/models" 32 "github.com/minio/madmin-go/v3" 33 "github.com/secure-io/sio-go" 34 ) 35 36 func registerInspectHandler(api *operations.ConsoleAPI) { 37 api.InspectInspectHandler = inspectApi.InspectHandlerFunc(func(params inspectApi.InspectParams, principal *models.Principal) middleware.Responder { 38 if v, err := base64.URLEncoding.DecodeString(params.File); err == nil && utf8.Valid(v) { 39 params.File = string(v) 40 } 41 42 if v, err := base64.URLEncoding.DecodeString(params.Volume); err == nil && utf8.Valid(v) { 43 params.Volume = string(v) 44 } 45 46 k, r, err := getInspectResult(principal, ¶ms) 47 if err != nil { 48 return inspectApi.NewInspectDefault(err.Code).WithPayload(err.APIError) 49 } 50 51 return middleware.ResponderFunc(processInspectResponse(¶ms, k, r)) 52 }) 53 } 54 55 func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) ([]byte, io.ReadCloser, *CodedAPIError) { 56 ctx := params.HTTPRequest.Context() 57 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 58 if err != nil { 59 return nil, nil, ErrorWithContext(ctx, err) 60 } 61 62 cfg := madmin.InspectOptions{ 63 File: params.File, 64 Volume: params.Volume, 65 } 66 67 // TODO: Remove encryption option and always encrypt. 68 // Maybe also add public key field. 69 if params.Encrypt != nil && *params.Encrypt { 70 cfg.PublicKey, _ = base64.StdEncoding.DecodeString("MIIBCgKCAQEAs/128UFS9A8YSJY1XqYKt06dLVQQCGDee69T+0Tip/1jGAB4z0/3QMpH0MiS8Wjs4BRWV51qvkfAHzwwdU7y6jxU05ctb/H/WzRj3FYdhhHKdzear9TLJftlTs+xwj2XaADjbLXCV1jGLS889A7f7z5DgABlVZMQd9BjVAR8ED3xRJ2/ZCNuQVJ+A8r7TYPGMY3wWvhhPgPk3Lx4WDZxDiDNlFs4GQSaESSsiVTb9vyGe/94CsCTM6Cw9QG6ifHKCa/rFszPYdKCabAfHcS3eTr0GM+TThSsxO7KfuscbmLJkfQev1srfL2Ii2RbnysqIJVWKEwdW05ID8ryPkuTuwIDAQAB") 71 } 72 73 // create a MinIO Admin Client interface implementation 74 // defining the client to be used 75 adminClient := AdminClient{Client: mAdmin} 76 77 k, r, err := adminClient.inspect(ctx, cfg) 78 if err != nil { 79 return nil, nil, ErrorWithContext(ctx, err) 80 } 81 return k, r, nil 82 } 83 84 // borrowed from mc cli 85 func decryptInspectV1(key [32]byte, r io.Reader) io.ReadCloser { 86 stream, err := sio.AES_256_GCM.Stream(key[:]) 87 if err != nil { 88 return nil 89 } 90 nonce := make([]byte, stream.NonceSize()) 91 return io.NopCloser(stream.DecryptReader(r, nonce, nil)) 92 } 93 94 func processInspectResponse(params *inspectApi.InspectParams, k []byte, r io.ReadCloser) func(w http.ResponseWriter, _ runtime.Producer) { 95 isEnc := params.Encrypt != nil && *params.Encrypt 96 return func(w http.ResponseWriter, _ runtime.Producer) { 97 ext := "enc" 98 if len(k) == 32 && !isEnc { 99 ext = "zip" 100 r = decryptInspectV1(*(*[32]byte)(k), r) 101 } 102 fileName := fmt.Sprintf("inspect-%s-%s.%s", params.Volume, params.File, ext) 103 fileName = strings.Map(func(r rune) rune { 104 switch { 105 case r >= 'A' && r <= 'Z': 106 return r 107 case r >= 'a' && r <= 'z': 108 return r 109 case r >= '0' && r <= '9': 110 return r 111 default: 112 if strings.ContainsAny(string(r), "-+._") { 113 return r 114 } 115 return '_' 116 } 117 }, fileName) 118 w.Header().Set("Content-Type", "application/octet-stream") 119 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName)) 120 121 _, err := io.Copy(w, r) 122 if err != nil { 123 LogError("unable to write all the data: %v", err) 124 } 125 } 126 }