github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/cmd/inspect/inspect.go (about) 1 /* 2 * Copyright (c) 2018-2020 vChain, Inc. All Rights Reserved. 3 * This software is released under GPL3. 4 * The full license information can be found under: 5 * https://www.gnu.org/licenses/gpl-3.0.en.html 6 * 7 */ 8 9 package inspect 10 11 import ( 12 "fmt" 13 "github.com/vchain-us/vcn/internal/errors" 14 "strings" 15 16 "github.com/spf13/viper" 17 "github.com/vchain-us/vcn/internal/assert" 18 "github.com/vchain-us/vcn/pkg/meta" 19 20 "github.com/vchain-us/vcn/pkg/cmd/internal/cli" 21 22 "github.com/spf13/cobra" 23 "github.com/vchain-us/vcn/pkg/api" 24 "github.com/vchain-us/vcn/pkg/cmd/internal/types" 25 "github.com/vchain-us/vcn/pkg/extractor" 26 "github.com/vchain-us/vcn/pkg/store" 27 ) 28 29 // NewCommand returns the cobra command for `vcn inspect` 30 func NewCommand() *cobra.Command { 31 cmd := &cobra.Command{ 32 Use: "inspect", 33 Aliases: []string{"i"}, 34 Short: "Returns the asset history with low-level information", 35 Long: ` 36 Returns the asset history with low-level information 37 38 Environment variables: 39 VCN_USER= 40 VCN_PASSWORD= 41 VCN_NOTARIZATION_PASSWORD= 42 VCN_NOTARIZATION_PASSWORD_EMPTY= 43 VCN_OTP= 44 VCN_OTP_EMPTY= 45 VCN_LC_HOST= 46 VCN_LC_PORT= 47 VCN_LC_CERT= 48 VCN_LC_SKIP_TLS_VERIFY=false 49 VCN_LC_NO_TLS=false 50 VCN_LC_API_KEY= 51 VCN_LC_LEDGER= 52 `, 53 PreRunE: func(cmd *cobra.Command, args []string) error { 54 return viper.BindPFlags(cmd.Flags()) 55 }, 56 RunE: runInspect, 57 Args: func(cmd *cobra.Command, args []string) error { 58 if hash, _ := cmd.Flags().GetString("hash"); hash != "" { 59 if len(args) > 0 { 60 return fmt.Errorf("cannot use ARG(s) with --hash") 61 } 62 return nil 63 } 64 65 first, _ := cmd.Flags().GetUint64("first") 66 last, _ := cmd.Flags().GetUint64("last") 67 start, _ := cmd.Flags().GetString("start") 68 end, _ := cmd.Flags().GetString("end") 69 70 if (first > 0 || last > 0 || start != "" || end != "") && 71 store.Config().CurrentContext.LcHost == "" { 72 return fmt.Errorf("time range filter is available only in Ledger Compliance environment") 73 } 74 75 if first > 0 && last > 0 { 76 return fmt.Errorf("--first and --last are mutual exclusive") 77 } 78 return cobra.MinimumNArgs(1)(cmd, args) 79 }, 80 Example: ` 81 vcn inspect document.pdf --last 1 82 vcn inspect document.pdf --first 1 83 vcn inspect document.pdf --start 2020/10/28-08:00:00 --end 2020/10/28-17:00:00 --first 10 84 vcn inspect document.pdf --signerID CygBE_zb8XnprkkO6ncIrbbwYoUq5T1zfyEF6DhqcAI= --start 2020/10/28-16:00:00 --end 2020/10/28-17:10:00 --last 3 85 `, 86 } 87 88 cmd.SetUsageTemplate( 89 strings.Replace(cmd.UsageTemplate(), "{{.UseLine}}", "{{.UseLine}} ARG", 1), 90 ) 91 92 cmd.Flags().String("hash", "", "specify a hash to inspect, if set no ARG can be used") 93 cmd.Flags().Bool("extract-only", false, "if set, print only locally extracted info") 94 // ledger compliance flags 95 cmd.Flags().String("lc-host", "", meta.VcnLcHostFlagDesc) 96 cmd.Flags().String("lc-port", "443", meta.VcnLcPortFlagDesc) 97 cmd.Flags().String("lc-cert", "", meta.VcnLcCertPathDesc) 98 cmd.Flags().Bool("lc-skip-tls-verify", false, meta.VcnLcSkipTlsVerifyDesc) 99 cmd.Flags().Bool("lc-no-tls", false, meta.VcnLcNoTlsDesc) 100 cmd.Flags().String("lc-api-key", "", meta.VcnLcApiKeyDesc) 101 cmd.Flags().String("lc-ledger", "", meta.VcnLcLedgerDesc) 102 103 cmd.Flags().String("signerID", "", "specify a signerID to refine inspection result on ledger compliance") 104 105 cmd.Flags().Uint64("first", 0, "set the limit for the first elements filter") 106 cmd.Flags().Uint64("last", 0, "set the limit for the last elements filter") 107 108 cmd.Flags().String("start", "", "set the start of date and time range filter. Example 2020/10/28-16:00:00") 109 cmd.Flags().String("end", "", "set the end of date and time range filter. Example 2020/10/28-16:00:00") 110 111 return cmd 112 } 113 114 func runInspect(cmd *cobra.Command, args []string) error { 115 116 output, err := cmd.Flags().GetString("output") 117 if err != nil { 118 return err 119 } 120 hash, err := cmd.Flags().GetString("hash") 121 if err != nil { 122 return err 123 } 124 hash = strings.ToLower(hash) 125 126 extractOnly, err := cmd.Flags().GetBool("extract-only") 127 if err != nil { 128 return err 129 } 130 cmd.SilenceUsage = true 131 132 if hash == "" { 133 if len(args) < 1 { 134 return fmt.Errorf("no argument") 135 } 136 if hash, err = extractInfo(args[0], output); err != nil { 137 return err 138 } 139 if output == "" { 140 fmt.Print("\n\n") 141 } 142 } 143 144 if extractOnly { 145 return nil 146 } 147 148 signerID, err := cmd.Flags().GetString("signerID") 149 if err != nil { 150 return err 151 } 152 153 lcHost := viper.GetString("lc-host") 154 lcPort := viper.GetString("lc-port") 155 lcCert := viper.GetString("lc-cert") 156 skipTlsVerify := viper.GetBool("lc-skip-tls-verify") 157 noTls := viper.GetBool("lc-no-tls") 158 lcApiKey := viper.GetString("lc-api-key") 159 lcLedger := viper.GetString("lc-ledger") 160 161 if lcApiKey == "" && lcHost != "" { 162 return errors.ErrNoLcApiKeyEnv 163 } 164 165 //check if an lcUser is present inside the context 166 var lcUser *api.LcUser 167 uif, err := api.GetUserFromContext(store.Config().CurrentContext, lcApiKey, lcLedger, nil) 168 if err != nil { 169 return err 170 } 171 if lctmp, ok := uif.(*api.LcUser); ok { 172 lcUser = lctmp 173 } 174 175 // It uses flags for CNC context client constructor if at least host and apikey are provided 176 if lcHost != "" && lcApiKey != "" { 177 // client from context could be override by the one created from local flags 178 lcUser, err = api.NewLcUser(lcApiKey, lcLedger, lcHost, lcPort, lcCert, skipTlsVerify, noTls, nil) 179 if err != nil { 180 return err 181 } // Store the new config 182 if err := store.SaveConfig(); err != nil { 183 return err 184 } 185 } 186 187 if lcUser != nil { 188 err = lcUser.Client.Connect() 189 if err != nil { 190 return err 191 } 192 first, err := cmd.Flags().GetUint64("first") 193 if err != nil { 194 return err 195 } 196 last, err := cmd.Flags().GetUint64("last") 197 if err != nil { 198 return err 199 } 200 start, err := cmd.Flags().GetString("start") 201 if err != nil { 202 return err 203 } 204 end, err := cmd.Flags().GetString("end") 205 if err != nil { 206 return err 207 } 208 209 if first == 0 && last == 0 { 210 last = 100 211 fmt.Printf("no filter is specified. At maximum last 100 items will be returned\n") 212 } 213 return lcInspect(hash, signerID, lcUser, first, last, start, end, output) 214 } 215 216 // User 217 if err := assert.UserLogin(); err != nil { 218 return err 219 } 220 u, ok := uif.(*api.User) 221 if !ok { 222 return fmt.Errorf("cannot load the current user") 223 } 224 225 if hasAuth, _ := u.IsAuthenticated(); hasAuth && output == "" { 226 fmt.Printf("Current user: %s\n", u.Email()) 227 } 228 229 return inspect(hash, u, output) 230 } 231 232 func extractInfo(arg string, output string) (hash string, err error) { 233 a, err := extractor.Extract([]string{arg}) 234 if err != nil { 235 return "", err 236 } 237 if len(a) == 0 { 238 return "", fmt.Errorf("unable to process the input asset provided: %s", arg) 239 } 240 if len(a) == 1 { 241 hash = a[0].Hash 242 } 243 if len(a) > 1 { 244 return "", fmt.Errorf("info extraction on multiple items is not yet supported") 245 } 246 if output == "" { 247 fmt.Printf("Extracted info from: %s\n\n", arg) 248 } 249 cli.Print(output, types.NewResult(a[0], nil, nil)) 250 return 251 } 252 253 func inspect(hash string, u *api.User, output string) error { 254 results, err := GetResults(hash, u) 255 if err != nil { 256 return err 257 } 258 259 if output == "" { 260 fmt.Printf( 261 `%d notarizations found for "%s" 262 263 `, 264 len(results), hash) 265 } 266 267 return cli.PrintSlice(output, results) 268 } 269 270 func GetResults(hash string, u *api.User) ([]types.Result, error) { 271 verifications, err := api.BlockChainInspect(hash) 272 if err != nil { 273 return nil, err 274 } 275 l := len(verifications) 276 277 results := make([]types.Result, l) 278 for i, v := range verifications { 279 ar, err := api.LoadArtifact(u, hash, v.MetaHash()) 280 results[i] = *types.NewResult(nil, ar, &v) 281 if err != nil { 282 results[i].AddError(err) 283 } 284 // check if artifact is synced, if any 285 if ar != nil { 286 if v.Status.String() != ar.Status { 287 results[i].AddError(fmt.Errorf( 288 "status not in sync (blockchain: %s, platform: %s)", v.Status.String(), ar.Status, 289 )) 290 } 291 if int64(v.Level) != ar.Level { 292 results[i].AddError(fmt.Errorf( 293 "level not in sync (blockchain: %d, platform: %d)", v.Level, ar.Level, 294 )) 295 } 296 } 297 } 298 return results, nil 299 }