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  }