github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/cmd/camput/init.go (about) 1 /* 2 Copyright 2011 Google Inc. 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 main 18 19 import ( 20 "encoding/json" 21 "flag" 22 "fmt" 23 "log" 24 "os" 25 "path/filepath" 26 27 "camlistore.org/pkg/blob" 28 "camlistore.org/pkg/client/android" 29 "camlistore.org/pkg/cmdmain" 30 "camlistore.org/pkg/jsonsign" 31 "camlistore.org/pkg/osutil" 32 "camlistore.org/pkg/types/clientconfig" 33 ) 34 35 type initCmd struct { 36 newKey bool // whether to create a new GPG ring and key. 37 noconfig bool // whether to generate a client config file. 38 keyId string // GPG key ID to use. 39 secretRing string // GPG secret ring file to use. 40 } 41 42 func init() { 43 cmdmain.RegisterCommand("init", func(flags *flag.FlagSet) cmdmain.CommandRunner { 44 cmd := new(initCmd) 45 flags.BoolVar(&cmd.newKey, "newkey", false, 46 "Automatically generate a new identity in a new secret ring at the default location (~/.config/camlistore/identity-secring.gpg on linux).") 47 flags.StringVar(&cmd.keyId, "gpgkey", "", "GPG key ID to use for signing (overrides $GPGKEY environment)") 48 flags.BoolVar(&cmd.noconfig, "noconfig", false, "Stop after creating the public key blob, and do not try and create a config file.") 49 return cmd 50 }) 51 } 52 53 func (c *initCmd) Describe() string { 54 return "Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring." 55 } 56 57 func (c *initCmd) Usage() { 58 fmt.Fprintf(cmdmain.Stderr, "Usage: camput init [opts]") 59 } 60 61 func (c *initCmd) Examples() []string { 62 return []string{ 63 "", 64 "--gpgkey=XXXXX", 65 "--newkey Creates a new identity", 66 } 67 } 68 69 // initSecretRing sets c.secretRing. It tries, in this order, the --secret-keyring flag, 70 // the CAMLI_SECRET_RING env var, then defaults to the operating system dependent location 71 // otherwise. 72 // It returns an error if the file does not exist. 73 func (c *initCmd) initSecretRing() error { 74 if secretRing, ok := osutil.ExplicitSecretRingFile(); ok { 75 c.secretRing = secretRing 76 } else { 77 if android.OnAndroid() { 78 panic("on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used.") 79 } 80 c.secretRing = osutil.SecretRingFile() 81 } 82 if _, err := os.Stat(c.secretRing); err != nil { 83 hint := "\nA GPG key is required, please use 'camput init --newkey'.\n\nOr if you know what you're doing, you can set the global camput flag --secret-keyring, or the CAMLI_SECRET_RING env var, to use your own GPG ring. And --gpgkey=<pubid> or GPGKEY to select which key ID to use." 84 return fmt.Errorf("Could not use secret ring file %v: %v.\n%v", c.secretRing, err, hint) 85 } 86 return nil 87 } 88 89 // initKeyId sets c.keyId. It checks, in this order, the --gpgkey flag, the GPGKEY env var, 90 // and in the default identity secret ring. 91 func (c *initCmd) initKeyId() error { 92 if k := c.keyId; k != "" { 93 return nil 94 } 95 if k := os.Getenv("GPGKEY"); k != "" { 96 c.keyId = k 97 return nil 98 } 99 100 k, err := jsonsign.KeyIdFromRing(c.secretRing) 101 if err != nil { 102 hint := "You can set --gpgkey=<pubid> or the GPGKEY env var to select which key ID to use.\n" 103 return fmt.Errorf("No suitable gpg key was found in %v: %v.\n%v", c.secretRing, err, hint) 104 } 105 c.keyId = k 106 log.Printf("Re-using identity with keyId %q found in file %s", c.keyId, c.secretRing) 107 return nil 108 } 109 110 func (c *initCmd) getPublicKeyArmored() ([]byte, error) { 111 entity, err := jsonsign.EntityFromSecring(c.keyId, c.secretRing) 112 if err != nil { 113 return nil, fmt.Errorf("Could not find keyId %v in ring %v: %v", c.keyId, c.secretRing, err) 114 } 115 pubArmor, err := jsonsign.ArmoredPublicKey(entity) 116 if err != nil { 117 return nil, fmt.Errorf("failed to export armored public key ID %q from %v: %v", c.keyId, c.secretRing, err) 118 } 119 return []byte(pubArmor), nil 120 } 121 122 func (c *initCmd) RunCommand(args []string) error { 123 if len(args) > 0 { 124 return cmdmain.ErrUsage 125 } 126 127 if c.newKey && c.keyId != "" { 128 log.Fatal("--newkey and --gpgkey are mutually exclusive") 129 } 130 131 var err error 132 if c.newKey { 133 c.secretRing = osutil.DefaultSecretRingFile() 134 c.keyId, err = jsonsign.GenerateNewSecRing(c.secretRing) 135 if err != nil { 136 return err 137 } 138 } else { 139 if err := c.initSecretRing(); err != nil { 140 return err 141 } 142 if err := c.initKeyId(); err != nil { 143 return err 144 } 145 } 146 147 pubArmor, err := c.getPublicKeyArmored() 148 if err != nil { 149 return err 150 } 151 152 bref := blob.SHA1FromString(string(pubArmor)) 153 154 log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String()) 155 156 if c.noconfig { 157 return nil 158 } 159 160 configFilePath := osutil.UserClientConfigPath() 161 _, err = os.Stat(configFilePath) 162 if err == nil { 163 log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath) 164 } 165 if err := os.MkdirAll(filepath.Dir(configFilePath), 0700); err != nil { 166 return err 167 } 168 if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil { 169 defer f.Close() 170 m := &clientconfig.Config{ 171 Servers: map[string]*clientconfig.Server{ 172 "localhost": { 173 Server: "http://localhost:3179", 174 IsDefault: true, 175 Auth: "localhost", 176 }, 177 }, 178 Identity: c.keyId, 179 IgnoredFiles: []string{".DS_Store"}, 180 } 181 182 jsonBytes, err := json.MarshalIndent(m, "", " ") 183 if err != nil { 184 log.Fatalf("JSON serialization error: %v", err) 185 } 186 _, err = f.Write(jsonBytes) 187 if err != nil { 188 log.Fatalf("Error writing to %q: %v", configFilePath, err) 189 } 190 log.Printf("Wrote %q; modify as necessary.", configFilePath) 191 } else { 192 return fmt.Errorf("could not write client config file %v: %v", configFilePath, err) 193 } 194 return nil 195 }