Merge pull request #46 from cloudflare/zi/simple-client

A simple client package for redoctober
This commit is contained in:
Nick Sullivan
2015-04-24 15:25:57 -07:00
3 changed files with 262 additions and 45 deletions

220
client/client.go Normal file
View File

@@ -0,0 +1,220 @@
package client
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"github.com/cloudflare/redoctober/core"
)
// RemoteServer represents a remote RedOctober server.
type RemoteServer struct {
client *http.Client
serverAddress string
}
// NewRemoteServer generates a RemoteServer with the server address and
// the root CA the server uses to authenticate itself.
func NewRemoteServer(serverAddress, CAFile string) (*RemoteServer, error) {
// populate a root CA pool from file
rootCAs := x509.NewCertPool()
pemBytes, err := ioutil.ReadFile(CAFile)
if err != nil {
return nil, errors.New("fail to read CA file: " + err.Error())
}
ok := rootCAs.AppendCertsFromPEM(pemBytes)
if !ok {
return nil, errors.New("fail to populate CA root pool.")
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: rootCAs},
DisableCompression: true,
}
server := &RemoteServer{
client: &http.Client{Transport: tr},
serverAddress: serverAddress,
}
return server, nil
}
// getURL creates URL for a specific path of the RemoteServer
func (c *RemoteServer) getURL(path string) string {
return fmt.Sprintf("https://%s%s", c.serverAddress, path)
}
// doAction sends req to the remote server and returns the response
func (c *RemoteServer) doAction(action string, req []byte) ([]byte, error) {
buf := bytes.NewBuffer(req)
url := c.getURL("/" + action)
resp, err := c.client.Post(url, "application/json", buf)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
resp.Body.Close()
return body, nil
}
// unmarshalResponseData is a helper function that unmarshal response bytes
// into ResponseData object.
func unmarshalResponseData(respBytes []byte) (*core.ResponseData, error) {
response := new(core.ResponseData)
err := json.Unmarshal(respBytes, response)
if err != nil {
return nil, err
}
if response.Status != "ok" {
return nil, errors.New(response.Status)
}
return response, nil
}
// Create creates an admin account at the remote server
func (c *RemoteServer) Create(req core.CreateRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("create", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}
// Summary returns the summary reported by the remote server
func (c *RemoteServer) Summary(req core.SummaryRequest) (*core.SummaryData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("summary", reqBytes)
if err != nil {
return nil, err
}
response := new(core.SummaryData)
err = json.Unmarshal(respBytes, response)
if err != nil {
return nil, err
}
if response.Status != "ok" {
return nil, errors.New(response.Status)
}
return response, nil
}
// Delegate issues a delegate request to the remote server
func (c *RemoteServer) Delegate(req core.DelegateRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("delegate", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}
// Modify issues a modify request to the remote server
func (c *RemoteServer) Modify(req core.ModifyRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("modify", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}
// Encrypt issues an encrypt request to the remote server
func (c *RemoteServer) Encrypt(req core.EncryptRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("encrypt", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}
// Decrypt issues an decrypt request to the remote server
func (c *RemoteServer) Decrypt(req core.DecryptRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("decrypt", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}
// DecryptIntoData issues an decrypt request to the remote server and extract
// the decrypted data from the response
func (c *RemoteServer) DecryptIntoData(req core.DecryptRequest) ([]byte, error) {
responseData, err := c.Decrypt(req)
if err != nil {
return nil, err
}
d := new(core.DecryptWithDelegates)
err = json.Unmarshal(responseData.Response, d)
if err != nil {
return nil, err
}
return d.Data, nil
}
// Password issues an password request to the remote server
func (c *RemoteServer) Password(req []byte) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("delegate", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}

View File

@@ -20,17 +20,17 @@ import (
// JSON that should be sent on the /delegate URI and it is handled by
// the Delegate function below).
type create struct {
type CreateRequest struct {
Name string
Password string
}
type summary struct {
type SummaryRequest struct {
Name string
Password string
}
type delegate struct {
type DelegateRequest struct {
Name string
Password string
@@ -40,14 +40,14 @@ type delegate struct {
Labels []string
}
type password struct {
type PasswordRequest struct {
Name string
Password string
NewPassword string
}
type encrypt struct {
type EncryptRequest struct {
Name string
Password string
@@ -60,19 +60,14 @@ type encrypt struct {
Labels []string
}
type decrypt struct {
type DecryptRequest struct {
Name string
Password string
Data []byte
}
type decryptWithDelegates struct {
Data []byte
Delegates []string
}
type modify struct {
type ModifyRequest struct {
Name string
Password string
@@ -82,34 +77,35 @@ type modify struct {
// These structures map the JSON responses that will be sent from the API
type status struct {
Status string
}
type responseData struct {
type ResponseData struct {
Status string
Response []byte
Response []byte `json:",omitempty"`
}
type summaryData struct {
type SummaryData struct {
Status string
Live map[string]keycache.ActiveUser
All map[string]passvault.Summary
}
type DecryptWithDelegates struct {
Data []byte
Delegates []string
}
// Helper functions that create JSON responses sent by core
func jsonStatusOk() ([]byte, error) {
return json.Marshal(status{Status: "ok"})
return json.Marshal(ResponseData{Status: "ok"})
}
func jsonStatusError(err error) ([]byte, error) {
return json.Marshal(status{Status: err.Error()})
return json.Marshal(ResponseData{Status: err.Error()})
}
func jsonSummary() ([]byte, error) {
return json.Marshal(summaryData{Status: "ok", Live: keycache.GetSummary(), All: passvault.GetSummary()})
return json.Marshal(SummaryData{Status: "ok", Live: keycache.GetSummary(), All: passvault.GetSummary()})
}
func jsonResponse(resp []byte) ([]byte, error) {
return json.Marshal(responseData{Status: "ok", Response: resp})
return json.Marshal(ResponseData{Status: "ok", Response: resp})
}
// validateAdmin checks that the username and password passed in are
@@ -156,7 +152,7 @@ func Init(path string) (err error) {
// Create processes a create request.
func Create(jsonIn []byte) ([]byte, error) {
var s create
var s CreateRequest
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
@@ -180,7 +176,7 @@ func Create(jsonIn []byte) ([]byte, error) {
// Summary processes a summary request.
func Summary(jsonIn []byte) ([]byte, error) {
var s summary
var s SummaryRequest
keycache.Refresh()
if err := json.Unmarshal(jsonIn, &s); err != nil {
@@ -201,7 +197,7 @@ func Summary(jsonIn []byte) ([]byte, error) {
// Delegate processes a delegation request.
func Delegate(jsonIn []byte) ([]byte, error) {
var s delegate
var s DelegateRequest
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
@@ -242,7 +238,7 @@ func Delegate(jsonIn []byte) ([]byte, error) {
// Password processes a password change request.
func Password(jsonIn []byte) ([]byte, error) {
var s password
var s PasswordRequest
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
@@ -262,7 +258,7 @@ func Password(jsonIn []byte) ([]byte, error) {
// Encrypt processes an encrypt request.
func Encrypt(jsonIn []byte) ([]byte, error) {
var s encrypt
var s EncryptRequest
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
@@ -288,9 +284,10 @@ func Encrypt(jsonIn []byte) ([]byte, error) {
// Decrypt processes a decrypt request.
func Decrypt(jsonIn []byte) ([]byte, error) {
var s decrypt
var s DecryptRequest
err := json.Unmarshal(jsonIn, &s)
if err != nil {
log.Println("Error unmarshaling input:", err)
return jsonStatusError(err)
}
@@ -305,7 +302,7 @@ func Decrypt(jsonIn []byte) ([]byte, error) {
return jsonStatusError(err)
}
resp := &decryptWithDelegates{
resp := &DecryptWithDelegates{
Data: data,
Delegates: names,
}
@@ -320,7 +317,7 @@ func Decrypt(jsonIn []byte) ([]byte, error) {
// Modify processes a modify request.
func Modify(jsonIn []byte) ([]byte, error) {
var s modify
var s ModifyRequest
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)

View File

@@ -25,7 +25,7 @@ func TestCreate(t *testing.T) {
t.Fatalf("Error in creating account, %v", err)
}
var s responseData
var s ResponseData
err = json.Unmarshal(respJson, &s)
if err != nil {
t.Fatalf("Error in creating account, %v", err)
@@ -60,7 +60,7 @@ func TestSummary(t *testing.T) {
if err != nil {
t.Fatalf("Error in summary of account with no vault, %v", err)
}
var s summaryData
var s SummaryData
err = json.Unmarshal(respJson, &s)
if err != nil {
t.Fatalf("Error in summary of account with no vault, %v", err)
@@ -181,7 +181,7 @@ func TestPassword(t *testing.T) {
Init("/tmp/db1.json")
// check for summary of initialized vault with new member
var s responseData
var s ResponseData
respJson, err := Create(createJson)
if err != nil {
t.Fatalf("Error in creating account, %v", err)
@@ -297,7 +297,7 @@ func TestEncryptDecrypt(t *testing.T) {
Init("/tmp/db1.json")
// check for summary of initialized vault with new member
var s responseData
var s ResponseData
respJson, err := Create(delegateJson)
if err != nil {
t.Fatalf("Error in creating account, %v", err)
@@ -340,7 +340,7 @@ func TestEncryptDecrypt(t *testing.T) {
if err != nil {
t.Fatalf("Error in summary, %v", err)
}
var sum summaryData
var sum SummaryData
err = json.Unmarshal(respJson, &sum)
if err != nil {
t.Fatalf("Error in summary, %v", err)
@@ -379,7 +379,7 @@ func TestEncryptDecrypt(t *testing.T) {
}
// decrypt file
decryptJson, err := json.Marshal(decrypt{Name: "Alice", Password: "Hello", Data: s.Response})
decryptJson, err := json.Marshal(DecryptRequest{Name: "Alice", Password: "Hello", Data: s.Response})
if err != nil {
t.Fatalf("Error in marshalling decryption, %v", err)
}
@@ -423,7 +423,7 @@ func TestEncryptDecrypt(t *testing.T) {
// verify the presence of the two delgations
keycache.Refresh()
var sum2 summaryData
var sum2 SummaryData
respJson, err = Summary(summaryJson)
if err != nil {
t.Fatalf("Error in summary, %v", err)
@@ -451,7 +451,7 @@ func TestEncryptDecrypt(t *testing.T) {
t.Fatalf("Error in decrypt, %v", s.Status)
}
var d decryptWithDelegates
var d DecryptWithDelegates
err = json.Unmarshal(s.Response, &d)
if err != nil {
t.Fatalf("Error in decrypt, %v", err)
@@ -488,7 +488,7 @@ func TestModify(t *testing.T) {
Init("/tmp/db1.json")
// check for summary of initialized vault with new member
var s responseData
var s ResponseData
respJson, err := Create(delegateJson)
if err != nil {
t.Fatalf("Error in creating account, %v", err)
@@ -531,7 +531,7 @@ func TestModify(t *testing.T) {
if err != nil {
t.Fatalf("Error in summary, %v", err)
}
var sum summaryData
var sum SummaryData
err = json.Unmarshal(respJson, &sum)
if err != nil {
t.Fatalf("Error in summary, %v", err)
@@ -638,7 +638,7 @@ func TestModify(t *testing.T) {
t.Fatalf("Error in modify, %v", s.Status)
}
var sum3 summaryData
var sum3 SummaryData
respJson, err = Summary(summaryJson2)
if err != nil {
t.Fatalf("Error in summary, %v", err)
@@ -683,7 +683,7 @@ func TestStatic(t *testing.T) {
Init("/tmp/db1.json")
// check for summary of initialized vault with new member
var s responseData
var s ResponseData
respJson, err := Delegate(delegateJson2)
if err != nil {
t.Fatalf("Error in delegating account, %v", err)
@@ -708,7 +708,7 @@ func TestStatic(t *testing.T) {
t.Fatalf("Error in delegating account, %v", s.Status)
}
var r responseData
var r ResponseData
respJson, err = Decrypt(decryptJson)
if err != nil {
t.Fatalf("Error in decrypt, %v", err)
@@ -721,7 +721,7 @@ func TestStatic(t *testing.T) {
t.Fatalf("Error in summary, %v", r.Status)
}
var d decryptWithDelegates
var d DecryptWithDelegates
err = json.Unmarshal(r.Response, &d)
if err != nil {
t.Fatalf("Error in decrypt, %v", err)