Files
redoctober/cmd/ro/main.go

246 lines
5.9 KiB
Go

package main
import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"github.com/cloudflare/redoctober/client"
"github.com/cloudflare/redoctober/cmd/ro/gopass"
"github.com/cloudflare/redoctober/core"
)
var action, user, pswd, userEnv, pswdEnv, server, caPath string
var owners, lefters, righters, inPath, labels, outPath, outEnv string
var uses int
var time, users string
type command struct {
Run func()
Desc string
}
var roServer *client.RemoteServer
var commandSet = map[string]command{
"create": command{Run: runCreate, Desc: "create a user account"},
"summary": command{Run: runSummary, Desc: "list the user and delegation summary"},
"delegate": command{Run: runDelegate, Desc: "do decryption delegation"},
"encrypt": command{Run: runEncrypt, Desc: "encrypt a file"},
"decrypt": command{Run: runDecrypt, Desc: "decrypt a file"},
"re-encrypt": command{Run: runReEncrypt, Desc: "re-encrypt a file"},
}
func registerFlags() {
flag.StringVar(&server, "server", "localhost:8080", "server address")
flag.StringVar(&caPath, "ca", "", "ca file path")
flag.StringVar(&owners, "owners", "", "comma separated owner list")
flag.StringVar(&users, "users", "", "comma separated user list")
flag.IntVar(&uses, "uses", 0, "number of delegated key uses")
flag.StringVar(&time, "time", "0h", "duration of delegated key uses")
flag.StringVar(&lefters, "left", "", "comma separated left owners")
flag.StringVar(&righters, "right", "", "comma separated right owners")
flag.StringVar(&labels, "labels", "", "comma separated labels")
flag.StringVar(&inPath, "in", "", "input data file")
flag.StringVar(&outPath, "out", "", "output data file")
flag.StringVar(&outEnv, "outenv", "", "env variable for output data")
flag.StringVar(&user, "user", "", "username")
flag.StringVar(&pswd, "password", "", "password")
flag.StringVar(&userEnv, "userenv", "RO_USER", "env variable for user name")
flag.StringVar(&pswdEnv, "pswdenv", "RO_PASS", "env variable for user password")
}
func getUserCredentials() {
user = os.Getenv(userEnv)
pswd = os.Getenv(pswdEnv)
if user == "" || pswd == "" {
fmt.Print("Username:")
fmt.Scan(&user)
var err error
pswd, err = gopass.GetPass("Password:")
processError(err)
}
}
func processError(err error) {
if err != nil {
log.Fatal("error:", err)
}
}
func processCSL(s string) []string {
if s == "" {
return nil
}
return strings.Split(s, ",")
}
func runCreate() {
req := core.CreateRequest{
Name: user,
Password: pswd,
}
resp, err := roServer.Create(req)
processError(err)
fmt.Println(resp.Status)
}
func runDelegate() {
req := core.DelegateRequest{
Name: user,
Password: pswd,
Uses: uses,
Time: time,
Users: processCSL(users),
Labels: processCSL(labels),
}
resp, err := roServer.Delegate(req)
processError(err)
fmt.Println(resp.Status)
}
// TODO: summary response needs better formatting
func runSummary() {
req := core.SummaryRequest{
Name: user,
Password: pswd,
}
resp, err := roServer.Summary(req)
processError(err)
fmt.Println(resp)
}
func runEncrypt() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
req := core.EncryptRequest{
Name: user,
Password: pswd,
Owners: processCSL(owners),
LeftOwners: processCSL(lefters),
RightOwners: processCSL(righters),
Labels: processCSL(labels),
Data: inBytes,
}
resp, err := roServer.Encrypt(req)
processError(err)
if resp.Status != "ok" {
log.Fatal("response status error:", resp.Status)
return
}
fmt.Println("Response Status:", resp.Status)
outBytes := []byte(base64.StdEncoding.EncodeToString(resp.Response))
ioutil.WriteFile(outPath, outBytes, 0644)
}
func runReEncrypt() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
// base64 decode the input
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
if err != nil {
log.Println("fail to base64 decode the data, proceed with raw data")
encBytes = inBytes
}
req := core.ReEncryptRequest{
Name: user,
Password: pswd,
Owners: processCSL(owners),
LeftOwners: processCSL(lefters),
RightOwners: processCSL(righters),
Labels: processCSL(labels),
Data: encBytes,
}
resp, err := roServer.ReEncrypt(req)
processError(err)
if resp.Status != "ok" {
log.Fatal("response status error:", resp.Status)
return
}
fmt.Println("Response Status:", resp.Status)
outBytes := []byte(base64.StdEncoding.EncodeToString(resp.Response))
ioutil.WriteFile(outPath, outBytes, 0644)
}
func runDecrypt() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
// base64 decode the input
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
if err != nil {
log.Println("fail to base64 decode the data, proceed with raw data")
encBytes = inBytes
}
req := core.DecryptRequest{
Name: user,
Password: pswd,
Data: encBytes,
}
resp, err := roServer.Decrypt(req)
processError(err)
if resp.Status != "ok" {
log.Fatal("response status error:", resp.Status)
return
}
fmt.Println("Response Status:", resp.Status)
var msg core.DecryptWithDelegates
err = json.Unmarshal(resp.Response, &msg)
processError(err)
fmt.Println("Secure:", msg.Secure)
fmt.Println("Delegates:", msg.Delegates)
ioutil.WriteFile(outPath, msg.Data, 0644)
}
func main() {
flag.Usage = func() {
fmt.Println("Usage: ro [options] subcommand")
fmt.Println("Currently supported subcommands are:")
for key := range commandSet {
fmt.Println("\t", key, ":", commandSet[key].Desc)
}
fmt.Println("Options:")
flag.PrintDefaults()
}
registerFlags()
flag.Parse()
if flag.NArg() != 1 {
flag.Usage()
os.Exit(1)
}
action := flag.Arg(0)
cmd, found := commandSet[action]
if !found {
fmt.Println("Unsupported subcommand:", action)
flag.Usage()
os.Exit(1)
} else {
var err error
roServer, err = client.NewRemoteServer(server, caPath)
processError(err)
getUserCredentials()
cmd.Run()
}
}