Add a status endpoint to the server.

This pull request adds a status endpoint to the Red October server; as
of this pull request, the status endpoint only returns the current
delegation persistence state. The HTTP UI has not been updated, as
this is scoped out for a future request; however, the CLI utility now
features a status command to fetch this information.
This commit is contained in:
Kyle Isom
2016-07-08 16:25:07 -07:00
parent 941cdb4e96
commit cb16b159f3
7 changed files with 174 additions and 16 deletions

View File

@@ -372,3 +372,18 @@ func (c *RemoteServer) OrderCancel(req core.OrderInfoRequest) (*core.ResponseDat
return unmarshalResponseData(respBytes) return unmarshalResponseData(respBytes)
} }
// Status returns the current delegation persistence state from the remote server.
func (c *RemoteServer) Status(req core.StatusRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}
respBytes, err := c.doAction("status", reqBytes)
if err != nil {
return nil, err
}
return unmarshalResponseData(respBytes)
}

View File

@@ -43,6 +43,7 @@ var commandSet = map[string]command{
"re-encrypt": command{Run: runReEncrypt, Desc: "re-encrypt a file"}, "re-encrypt": command{Run: runReEncrypt, Desc: "re-encrypt a file"},
"order": command{Run: runOrder, Desc: "place an order for delegations"}, "order": command{Run: runOrder, Desc: "place an order for delegations"},
"owners": command{Run: runOwner, Desc: "show owners list"}, "owners": command{Run: runOwner, Desc: "show owners list"},
"status": command{Run: runStatus, Desc: "show Red October persistent delegation state"},
} }
func registerFlags() { func registerFlags() {
@@ -260,6 +261,19 @@ func runOwner() {
fmt.Println(resp) fmt.Println(resp)
} }
func runStatus() {
req := core.StatusRequest{
Name: user,
Password: pswd,
}
resp, err := roServer.Status(req)
processError(err)
fmt.Println(resp.Status)
fmt.Println(resp)
}
func main() { func main() {
flag.Usage = func() { flag.Usage = func() {
fmt.Println("Usage: ro [options] subcommand") fmt.Println("Usage: ro [options] subcommand")

View File

@@ -84,6 +84,24 @@ func (hc *HipChat) Merge(other *HipChat) {
setIfNotEmpty(&hc.APIKey, other.APIKey) setIfNotEmpty(&hc.APIKey, other.APIKey)
} }
// Valid returns true if the HipChat config is ready to be used for
// HipChat notifications.
func (hc *HipChat) Valid() bool {
if hc.APIKey == "" {
return false
}
if hc.Room == "" {
return false
}
if hc.Host == "" {
return false
}
return true
}
// Metrics contains the configuration for the Prometheus metrics // Metrics contains the configuration for the Prometheus metrics
// collector. // collector.
type Metrics struct { type Metrics struct {

View File

@@ -247,3 +247,25 @@ func TestValid(t *testing.T) {
t.Fatal("config should be valid") t.Fatal("config should be valid")
} }
} }
func TestHipChatValid(t *testing.T) {
hc := &HipChat{}
if hc.Valid() {
t.Fatal("empty hipchat config shouldn't be valid")
}
hc.APIKey = "test"
if hc.Valid() {
t.Fatal("invalid hipchat config shouldn't be valid")
}
hc.Room = "test"
if hc.Valid() {
t.Fatal("invalid hipchat config shouldn't be valid")
}
hc.Host = "test"
if !hc.Valid() {
t.Fatal("valid hipchat config marked as invalid")
}
}

View File

@@ -12,6 +12,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/cloudflare/redoctober/config"
"github.com/cloudflare/redoctober/cryptor" "github.com/cloudflare/redoctober/cryptor"
"github.com/cloudflare/redoctober/hipchat" "github.com/cloudflare/redoctober/hipchat"
"github.com/cloudflare/redoctober/keycache" "github.com/cloudflare/redoctober/keycache"
@@ -129,6 +130,7 @@ type OrderInfoRequest struct {
OrderNum string OrderNum string
} }
type OrderOutstandingRequest struct { type OrderOutstandingRequest struct {
Name string Name string
Password string Password string
@@ -141,6 +143,11 @@ type OrderCancelRequest struct {
OrderNum string OrderNum string
} }
type StatusRequest struct {
Name string
Password string
}
// These structures map the JSON responses that will be sent from the API // These structures map the JSON responses that will be sent from the API
type ResponseData struct { type ResponseData struct {
@@ -166,6 +173,23 @@ type OwnersData struct {
Predicate string Predicate string
} }
type StatusData struct {
Status string
}
// Delegation restoration and persistance configuration follows.
const (
PDStateNeverPersist = "disabled"
PDStateNotPersisting = "inactive"
PDStateNowPersisting = "active"
)
var restore struct {
Config *config.Delegations
State string
}
// Helper functions that create JSON responses sent by core // Helper functions that create JSON responses sent by core
func jsonStatusOk() ([]byte, error) { func jsonStatusOk() ([]byte, error) {
@@ -217,7 +241,7 @@ func validateName(name, password string) error {
} }
// Init reads the records from disk from a given path // Init reads the records from disk from a given path
func Init(path, hcKey, hcRoom, hcHost, roHost string) error { func Init(path string, config *config.Config) error {
var err error var err error
defer func() { defer func() {
@@ -233,18 +257,24 @@ func Init(path, hcKey, hcRoom, hcHost, roHost string) error {
} }
var hipchatClient hipchat.HipchatClient var hipchatClient hipchat.HipchatClient
if hcKey != "" && hcRoom != "" && hcHost != "" { hc := config.HipChat
roomId, err := strconv.Atoi(hcRoom) if hc.Valid() {
roomId, err := strconv.Atoi(hc.Room)
if err != nil { if err != nil {
return errors.New("core.init unable to use hipchat roomId provided") return errors.New("core.init unable to use hipchat roomId provided")
} }
hipchatClient = hipchat.HipchatClient{ hipchatClient = hipchat.HipchatClient{
ApiKey: hcKey, ApiKey: hc.APIKey,
RoomId: roomId, RoomId: roomId,
HcHost: hcHost, HcHost: hc.Host,
RoHost: roHost, RoHost: config.UI.Root,
} }
} }
restore.Config = config.Delegations
restore.State = PDStateNeverPersist
orders = order.NewOrderer(hipchatClient) orders = order.NewOrderer(hipchatClient)
cache = keycache.Cache{UserKeys: make(map[keycache.DelegateIndex]keycache.ActiveUser)} cache = keycache.Cache{UserKeys: make(map[keycache.DelegateIndex]keycache.ActiveUser)}
crypt = cryptor.New(&records, &cache) crypt = cryptor.New(&records, &cache)
@@ -903,3 +933,32 @@ func OrderCancel(jsonIn []byte) (out []byte, err error) {
err = errors.New("Invalid Order Number") err = errors.New("Invalid Order Number")
return jsonStatusError(err) return jsonStatusError(err)
} }
// Status returns the current delegation persistence state. In the
// future, this may return more data.
func Status(jsonIn []byte) (out []byte, err error) {
var req StatusRequest
defer func() {
if err != nil {
log.Printf("core.status failed: user=%s %v", req.Name, err)
} else {
log.Printf("core.status success: user=%s", req.Name)
}
}()
if err = json.Unmarshal(jsonIn, &req); err != nil {
return jsonStatusError(err)
}
if err := validateUser(req.Name, req.Password, false); err != nil {
return jsonStatusError(err)
}
resp := StatusData{Status: restore.State}
if out, err = json.Marshal(resp); err != nil {
return jsonStatusError(err)
}
return
}

File diff suppressed because one or more lines are too long

View File

@@ -45,6 +45,7 @@ var functions = map[string]func([]byte) ([]byte, error){
"/orderout": core.OrdersOutstanding, "/orderout": core.OrdersOutstanding,
"/orderinfo": core.OrderInfo, "/orderinfo": core.OrderInfo,
"/ordercancel": core.OrderCancel, "/ordercancel": core.OrderCancel,
"/status": core.Status,
} }
type userRequest struct { type userRequest struct {
@@ -290,7 +291,7 @@ func main() {
os.Exit(2) os.Exit(2)
} }
if err := core.Init(vaultPath, cfg.HipChat.APIKey, cfg.HipChat.Room, cfg.HipChat.Room, cfg.UI.Root); err != nil { if err := core.Init(vaultPath, cfg); err != nil {
log.Fatal(err) log.Fatal(err)
} }