Files
redoctober/core/core.go

1225 lines
28 KiB
Go

// Package core handles the main operations of the Red October server.
//
// Copyright (c) 2013 CloudFlare, Inc.
package core
import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"
"github.com/cloudflare/redoctober/config"
"github.com/cloudflare/redoctober/cryptor"
"github.com/cloudflare/redoctober/hipchat"
"github.com/cloudflare/redoctober/keycache"
"github.com/cloudflare/redoctober/order"
"github.com/cloudflare/redoctober/passvault"
"github.com/cloudflare/redoctober/persist"
"github.com/cloudflare/redoctober/report"
"golang.org/x/crypto/ssh"
)
var (
crypt *cryptor.Cryptor
records passvault.Records
orders order.Orderer
)
// Each of these structures corresponds to the JSON expected on the
// correspondingly named URI (e.g. the delegate structure maps to the
// JSON that should be sent on the /delegate URI and it is handled by
// the Delegate function below).
type CreateRequest struct {
Name string
Password string
}
type SummaryRequest struct {
Name string
Password string
}
type PurgeRequest struct {
Name string
Password string
}
type DelegateRequest struct {
Name string
Password string
Uses int
Time string
Slot string
Users []string
Labels []string
}
type CreateUserRequest struct {
Name string
Password string
UserType string
HipchatName string
}
type PasswordRequest struct {
Name string
Password string
NewPassword string
HipchatName string
}
type EncryptRequest struct {
Name string
Password string
Minimum int
Owners []string
LeftOwners []string
RightOwners []string
Predicate string
Data []byte
Labels []string
Usages []string
}
type ReEncryptRequest EncryptRequest
type DecryptRequest struct {
Name string
Password string
Data []byte
}
type SSHSignWithRequest struct {
Name string
Password string
Data []byte
TBSData []byte
}
type SSHSignatureWithDelegates struct {
SignatureFormat string
Signature []byte
Secure bool
Delegates []string
}
type OwnersRequest struct {
Data []byte
}
type ModifyRequest struct {
Name string
Password string
ToModify string
Command string
}
type ExportRequest struct {
Name string
Password string
}
type OrderRequest struct {
Name string
Password string
Duration string
Uses int
Users []string
EncryptedData []byte
Labels []string
}
type OrderInfoRequest struct {
Name string
Password string
OrderNum string
}
type OrderOutstandingRequest struct {
Name string
Password string
}
type OrderCancelRequest struct {
Name string
Password string
OrderNum string
}
type StatusRequest struct {
Name string
Password string
}
// These structures map the JSON responses that will be sent from the API
type ResponseData struct {
Status string
Response []byte `json:",omitempty"`
}
type SummaryData struct {
Status string
State string
Live map[string]ActiveUser
All map[string]passvault.Summary
}
type ActiveUser struct {
keycache.Usage
AltNames map[string]string
Admin bool
Type string
}
type DecryptWithDelegates struct {
Data []byte
Secure bool
Delegates []string
}
type OwnersData struct {
Status string
Owners []string
Labels []string
Predicate string
}
type StatusData struct {
Status string
}
// Helper functions that create JSON responses sent by core
func jsonStatusOk() ([]byte, error) {
return json.Marshal(ResponseData{Status: "ok"})
}
func jsonStatusError(err error) ([]byte, error) {
return json.Marshal(ResponseData{Status: err.Error()})
}
func jsonSummary() ([]byte, error) {
state := crypt.Status()
return json.Marshal(SummaryData{Status: "ok", State: state.State, Live: liveSummary(), All: records.GetSummary()})
}
func jsonResponse(resp []byte) ([]byte, error) {
return json.Marshal(ResponseData{Status: "ok", Response: resp})
}
// validateUser checks that the username and password passed in are
// correct. If admin is true, the user must be an admin as well.
func validateUser(name, password string, admin bool) error {
if records.NumRecords() == 0 {
return errors.New("Vault is not created yet")
}
pr, ok := records.GetRecord(name)
if !ok {
return errors.New("User not present")
}
if err := pr.ValidatePassword(password); err != nil {
return err
}
if admin && !pr.IsAdmin() {
return errors.New("Admin required")
}
return nil
}
//Username must start with an alphanumeric character and can include "-" and "_" after the first
var validName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9\_\-]*$`).MatchString
// validateName checks that the username and password pass a validation test.
func validateName(name, password string) error {
if !validName(name) {
return errors.New("must start with an alphanumeric character and can include \"-\" or \"_\" after the first character")
}
if password == "" {
return errors.New("Password must be at least one character")
}
return nil
}
// liveSummary creates a sanitized version of cryptor.LiveSummary() without any key material
func liveSummary() map[string]ActiveUser {
currLiveSummary := crypt.LiveSummary()
summaryData := make(map[string]ActiveUser)
for summaryInfo, activeUser := range currLiveSummary {
sanitizedActiveUser := ActiveUser{
Usage: activeUser.Usage,
AltNames: activeUser.AltNames,
Admin: activeUser.Admin,
Type: activeUser.Type,
}
summaryData[summaryInfo] = sanitizedActiveUser
}
return summaryData
}
// Init reads the records from disk from a given path
func Init(path string, config *config.Config) error {
var err error
tags := map[string]string{"function": "core.Init"}
defer func() {
if err != nil {
report.Check(err, tags)
log.Printf("core.init failed: %v", err)
} else {
log.Printf("core.init success: path=%s", path)
}
}()
if records, err = passvault.InitFrom(path); err != nil {
err = fmt.Errorf("failed to load password vault %s: %s", path, err)
}
crypt, err = cryptor.New(&records, nil, config)
if err != nil {
return err
}
var hipchatClient hipchat.HipchatClient
hc := config.HipChat
if hc.Valid() {
roomId, err := strconv.Atoi(hc.Room)
if err != nil {
return errors.New("core.init unable to use hipchat roomId provided")
}
hipchatClient = hipchat.HipchatClient{
ApiKey: hc.APIKey,
RoomId: roomId,
HcHost: hc.Host,
RoHost: config.UI.Root,
}
name := hc.ID
if name == "" {
name = "Red October"
}
message := name + " has restarted."
color := hipchat.GreenBackground
status := crypt.Status()
if status.State == persist.Inactive {
message += " @here: persistence is currently " + status.State + "; the restore admins need to restore the saved delegations."
color = hipchat.RedBackground
}
err = hipchatClient.Notify(message, color)
if err != nil {
return err
}
}
orders = order.NewOrderer(hipchatClient)
return nil
}
// Create processes a create request.
func Create(jsonIn []byte) ([]byte, error) {
var s CreateRequest
var err error
defer func() {
if err != nil {
log.Printf("core.create failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.create success: user=%s", s.Name)
}
}()
if err = json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
if records.NumRecords() != 0 {
err = errors.New("Vault is already created")
return jsonStatusError(err)
}
// Validate the Name and Password as valid
if err = validateName(s.Name, s.Password); err != nil {
return jsonStatusError(err)
}
if _, err = records.AddNewRecord(s.Name, s.Password, true, passvault.DefaultRecordType); err != nil {
return jsonStatusError(err)
}
return jsonStatusOk()
}
// Summary processes a summary request.
func Summary(jsonIn []byte) ([]byte, error) {
var s SummaryRequest
var err error
defer func() {
if err != nil {
log.Printf("core.summary failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.summary success: user=%s", s.Name)
}
}()
err = crypt.Refresh()
if err != nil {
return jsonStatusError(err)
}
if err := json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
if records.NumRecords() == 0 {
err = errors.New("vault has not been created")
return jsonStatusError(err)
}
if err := validateUser(s.Name, s.Password, false); err != nil {
return jsonStatusError(err)
}
return jsonSummary()
}
// Purge processes a delegation purge request.
func Purge(jsonIn []byte) ([]byte, error) {
var s PurgeRequest
var err error
defer func() {
if err != nil {
log.Printf("core.purge failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.purge success: user=%s", s.Name)
}
}()
if err = json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
if records.NumRecords() == 0 {
err = errors.New("vault has not been created")
return jsonStatusError(err)
}
// Validate the Name and Password as valid and admin
if err = validateUser(s.Name, s.Password, true); err != nil {
return jsonStatusError(err)
}
err = crypt.Flush()
if err != nil {
return jsonStatusError(err)
}
return jsonStatusOk()
}
// Delegate processes a delegation request.
func Delegate(jsonIn []byte) ([]byte, error) {
var s DelegateRequest
var err error
var tags = map[string]string{"function": "core.Delegate"}
defer func() {
if err != nil {
tags["delegation.name"] = s.Name
tags["delegation.uses"] = fmt.Sprintf("%d", s.Uses)
tags["delegation.time"] = s.Time
tags["delegation.users"] = strings.Join(s.Users, ", ")
tags["delegation.labels"] = strings.Join(s.Labels, ", ")
log.Printf("core.delegate failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.delegate success: user=%s uses=%d time=%s users=%v labels=%v", s.Name, s.Uses, s.Time, s.Users, s.Labels)
}
}()
if err = json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
if records.NumRecords() == 0 {
err = errors.New("Vault is not created yet")
return jsonStatusError(err)
}
// Validate the Name and Password as valid
if err = validateName(s.Name, s.Password); err != nil {
return jsonStatusError(err)
}
// Make sure the user we are delegating to exists
var invalidUsers []string
for _, user := range s.Users {
if _, ok := records.GetRecord(user); !ok {
invalidUsers = append(invalidUsers, user)
}
}
if len(invalidUsers) != 0 {
err = fmt.Errorf("User(s) not present: %s", strings.Join(invalidUsers, ", "))
return jsonStatusError(err)
}
// Find password record for user and verify that their password
// matches. If not found then add a new entry for this user.
pr, found := records.GetRecord(s.Name)
if found {
if err = pr.ValidatePassword(s.Password); err != nil {
return jsonStatusError(err)
}
} else {
if pr, err = records.AddNewRecord(s.Name, s.Password, false, passvault.DefaultRecordType); err != nil {
return jsonStatusError(err)
}
}
// add signed-in record to active set
if err = crypt.Delegate(pr, s.Name, s.Password, s.Users, s.Labels, s.Uses, s.Slot, s.Time); err != nil {
return jsonStatusError(err)
}
// Make sure we capture the number who have already delegated.
for _, delegatedUser := range s.Users {
if orderKey, found := orders.FindOrder(delegatedUser, s.Labels); found {
order := orders.Orders[orderKey]
// Don't re-add names to the list of people who have delegated. Instead
// just skip them but make sure we count their delegation
if len(order.OwnersDelegated) == 0 {
order.OwnersDelegated = append(order.OwnersDelegated, s.Name)
} else {
for _, ownerName := range order.OwnersDelegated {
if ownerName == s.Name {
continue
}
order.OwnersDelegated = append(order.OwnersDelegated, s.Name)
order.Delegated++
}
}
orders.Orders[orderKey] = order
// Notify the hipchat room that there was a new delegator
orders.NotifyDelegation(s.Name, delegatedUser, orderKey, s.Time, s.Labels)
}
}
return jsonStatusOk()
}
// CreateUser processes a create-user request.
func CreateUser(jsonIn []byte) ([]byte, error) {
var s CreateUserRequest
var err error
defer func() {
if err != nil {
log.Printf("core.create-user failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.create-user success: user=%s", s.Name)
}
}()
if err = json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
// If no UserType if provided use the default one
if s.UserType == "" {
s.UserType = passvault.DefaultRecordType
}
if records.NumRecords() == 0 {
err = errors.New("Vault is not created yet")
return jsonStatusError(err)
}
// Validate the Name and Password as valid
if err = validateName(s.Name, s.Password); err != nil {
return jsonStatusError(err)
}
_, found := records.GetRecord(s.Name)
if found {
err = errors.New("User with that name already exists")
return jsonStatusError(err)
}
if _, err := records.AddNewRecord(s.Name, s.Password, false, s.UserType); err != nil {
return jsonStatusError(err)
}
if err = records.ChangePassword(s.Name, s.Password, "", s.HipchatName); err != nil {
return jsonStatusError(err)
}
return jsonStatusOk()
}
// Password processes a password change request.
func Password(jsonIn []byte) ([]byte, error) {
var err error
var s PasswordRequest
defer func() {
if err != nil {
log.Printf("core.password failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.password success: user=%s", s.Name)
}
}()
if err = json.Unmarshal(jsonIn, &s); err != nil {
return jsonStatusError(err)
}
if records.NumRecords() == 0 {
err = errors.New("Vault is not created yet")
return jsonStatusError(err)
}
// add signed-in record to active set
err = records.ChangePassword(s.Name, s.Password, s.NewPassword, s.HipchatName)
if err != nil {
return jsonStatusError(err)
}
return jsonStatusOk()
}
// Encrypt processes an encrypt request.
func Encrypt(jsonIn []byte) ([]byte, error) {
var s EncryptRequest
var err error
var tags = map[string]string{"function": "core.Encrypt"}
defer func() {
if err != nil {
tags["encrypt.user"] = s.Name
tags["encrypt.size"] = fmt.Sprintf("%d", len(s.Data))
report.Check(err, tags)
log.Printf("core.encrypt failed: user=%s size=%d %v", s.Name, len(s.Data), err)
} else {
log.Printf("core.encrypt success: user=%s size=%d", s.Name, len(s.Data))
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
if err = validateUser(s.Name, s.Password, false); err != nil {
return jsonStatusError(err)
}
access := cryptor.AccessStructure{
Minimum: s.Minimum,
Names: s.Owners,
LeftNames: s.LeftOwners,
RightNames: s.RightOwners,
Predicate: s.Predicate,
}
resp, err := crypt.Encrypt(s.Data, s.Labels, s.Usages, access)
if err != nil {
return jsonStatusError(err)
}
return jsonResponse(resp)
}
// ReEncrypt processes an Re-encrypt request.
func ReEncrypt(jsonIn []byte) ([]byte, error) {
var s ReEncryptRequest
var err error
defer func() {
if err != nil {
log.Printf("core.re-encrypt failed: user=%s size=%d %v", s.Name, len(s.Data), err)
} else {
log.Printf("core.re-encrypt success: user=%s size=%d", s.Name, len(s.Data))
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
if err = validateUser(s.Name, s.Password, false); err != nil {
return jsonStatusError(err)
}
data, _, _, usages, secure, err := crypt.Decrypt(s.Data, s.Name)
if err != nil {
return jsonStatusError(err)
}
if !secure {
return jsonStatusError(errors.New("decryption's secure bit is false"))
}
access := cryptor.AccessStructure{
Minimum: s.Minimum,
Names: s.Owners,
LeftNames: s.LeftOwners,
RightNames: s.RightOwners,
Predicate: s.Predicate,
}
resp, err := crypt.Encrypt(data, s.Labels, usages, access)
if err != nil {
return jsonStatusError(err)
}
return jsonResponse(resp)
}
// Decrypt processes a decrypt request.
func Decrypt(jsonIn []byte) ([]byte, error) {
var s DecryptRequest
var err error
var tags = map[string]string{"function": "core.Decrypt"}
defer func() {
if err != nil {
tags["decrypt.user"] = s.Name
report.Check(err, tags)
log.Printf("core.decrypt failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.decrypt success: user=%s", s.Name)
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
err = validateUser(s.Name, s.Password, false)
if err != nil {
return jsonStatusError(err)
}
data, allLabels, names, usages, secure, err := crypt.Decrypt(s.Data, s.Name)
if err != nil {
return jsonStatusError(err)
}
if len(usages) != 0 {
// a file must be marked as usable for "decrypt" before we will decrypt
// it for the user. If no usages are provided, we permit decryption
foundDecrypt := false
for _, usage := range usages {
if usage == "decrypt" {
foundDecrypt = true
break
}
}
if !foundDecrypt {
return jsonStatusError(errors.New("cannot decrypt this file"))
}
}
resp := &DecryptWithDelegates{
Data: data,
Secure: secure,
Delegates: names,
}
tags["delegates"] = strings.Join(names, ", ")
out, err := json.Marshal(resp)
if err != nil {
return jsonStatusError(err)
}
// Cleanup any orders that have been fulfilled and notify the room.
if orderKey, found := orders.FindOrder(s.Name, allLabels); found {
delete(orders.Orders, orderKey)
orders.NotifyOrderFulfilled(s.Name, orderKey)
}
return jsonResponse(out)
}
// SSHSignWith signs a message with an SSH key
func SSHSignWith(jsonIn []byte) ([]byte, error) {
var s SSHSignWithRequest
var err error
var tags = map[string]string{"function": "core.SSHSignWith"}
defer func() {
if err != nil {
tags["ssh-sign-with.user"] = s.Name
report.Check(err, tags)
log.Printf("core.ssh-sign-with failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.ssh-sign-with success: user=%s", s.Name)
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
err = validateUser(s.Name, s.Password, false)
if err != nil {
return jsonStatusError(err)
}
data, allLabels, names, usages, secure, err := crypt.Decrypt(s.Data, s.Name)
if err != nil {
return jsonStatusError(err)
}
// a file must be marked as usable for "ssh-sign-with" before we will try to sign with it
foundSign := false
for _, usage := range usages {
if usage == "ssh-sign-with" {
foundSign = true
break
}
}
if !foundSign {
return jsonStatusError(errors.New("cannot sign with this file"))
}
var signer ssh.Signer
// TODO ParsePrivateKeyWithPassphrase
signer, err = ssh.ParsePrivateKey(data)
if err != nil {
return jsonStatusError(err)
}
var signature *ssh.Signature
signature, err = signer.Sign(rand.Reader, s.TBSData)
resp := &SSHSignatureWithDelegates{
SignatureFormat: signature.Format,
Signature: signature.Blob,
Secure: secure,
Delegates: names,
}
tags["delegates"] = strings.Join(names, ", ")
out, err := json.Marshal(resp)
if err != nil {
return jsonStatusError(err)
}
// Cleanup any orders that have been fulfilled and notify the room.
if orderKey, found := orders.FindOrder(s.Name, allLabels); found {
delete(orders.Orders, orderKey)
orders.NotifyOrderFulfilled(s.Name, orderKey)
}
return jsonResponse(out)
}
// Modify processes a modify request.
func Modify(jsonIn []byte) ([]byte, error) {
var s ModifyRequest
var err error
defer func() {
if err != nil {
log.Printf("core.modify failed: user=%s target=%s command=%s %v", s.Name, s.ToModify, s.Command, err)
} else {
log.Printf("core.modify success: user=%s target=%s command=%s", s.Name, s.ToModify, s.Command)
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
if err = validateUser(s.Name, s.Password, true); err != nil {
return jsonStatusError(err)
}
if _, ok := records.GetRecord(s.ToModify); !ok {
err = errors.New("core: record to modify missing")
return jsonStatusError(err)
}
if s.Name == s.ToModify {
err = errors.New("core: cannot modify own record")
return jsonStatusError(err)
}
switch s.Command {
case "delete":
err = records.DeleteRecord(s.ToModify)
case "revoke":
err = records.RevokeRecord(s.ToModify)
case "admin":
err = records.MakeAdmin(s.ToModify)
default:
err = fmt.Errorf("core: unknown command '%s' passed to modify", s.Command)
return jsonStatusError(err)
}
if err != nil {
return jsonStatusError(err)
}
return jsonStatusOk()
}
// Owners processes a owners request.
func Owners(jsonIn []byte) ([]byte, error) {
var s OwnersRequest
var err error
defer func() {
if err != nil {
log.Printf("core.owners failed: size=%d %v", len(s.Data), err)
} else {
log.Printf("core.owners success: size=%d", len(s.Data))
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
names, labels, predicate, err := crypt.GetOwners(s.Data)
if err != nil {
return jsonStatusError(err)
}
return json.Marshal(OwnersData{
Status: "ok",
Owners: names,
Predicate: predicate,
Labels: labels,
})
}
// Export returns a backed up vault.
func Export(jsonIn []byte) ([]byte, error) {
var s ExportRequest
var err error
defer func() {
if err != nil {
log.Printf("core.export failed: user=%s %v", s.Name, err)
} else {
log.Printf("core.export success: user=%s", s.Name)
}
}()
err = json.Unmarshal(jsonIn, &s)
if err != nil {
return jsonStatusError(err)
}
err = validateUser(s.Name, s.Password, true)
if err != nil {
return jsonStatusError(err)
}
out, err := json.Marshal(records)
if err != nil {
return jsonStatusError(err)
}
return jsonResponse(out)
}
// Order will request delegations from other users.
func Order(jsonIn []byte) (out []byte, err error) {
var o OrderRequest
defer func() {
if err != nil {
log.Printf("core.order failed: user=%s %v", o.Name, err)
} else {
log.Printf("core.order success: user=%s", o.Name)
}
}()
if err = json.Unmarshal(jsonIn, &o); err != nil {
return jsonStatusError(err)
}
if err := validateUser(o.Name, o.Password, false); err != nil {
return jsonStatusError(err)
}
// Get the owners of the ciphertext.
owners, _, _, err := crypt.GetOwners(o.EncryptedData)
if err != nil {
return jsonStatusError(err)
}
if o.Duration == "" {
err = errors.New("Duration required when placing an order.")
return jsonStatusError(err)
}
if o.Uses == 0 {
err = errors.New("Number of required uses necessary when placing an order.")
return jsonStatusError(err)
}
err = crypt.Refresh()
if err != nil {
return jsonStatusError(err)
}
orderNum := order.GenerateNum()
if len(o.Users) == 0 {
err = errors.New("Must specify at least one user per order.")
return jsonStatusError(err)
}
adminsDelegated, numDelegated := crypt.DelegateStatus(o.Users[0], o.Labels, owners)
duration, err := time.ParseDuration(o.Duration)
if err != nil {
return jsonStatusError(err)
}
currentTime := time.Now()
ord := order.CreateOrder(o.Name,
orderNum,
currentTime,
duration,
adminsDelegated,
owners,
o.Users,
o.Labels,
numDelegated)
orders.Orders[orderNum] = ord
out, err = json.Marshal(ord)
// Get a map to any alternative name we want to notify
altOwners := records.GetAltNamesFromName(orders.AlternateName, owners)
// Let everyone on hipchat know there is a new order.
orders.NotifyNewOrder(o.Duration, orderNum, o.Users, o.Labels, o.Uses, altOwners)
if err != nil {
return jsonStatusError(err)
}
return jsonResponse(out)
}
// OrdersOutstanding will return a list of currently outstanding orders.
func OrdersOutstanding(jsonIn []byte) (out []byte, err error) {
var o OrderOutstandingRequest
defer func() {
if err != nil {
log.Printf("core.ordersout failed: user=%s %v", o.Name, err)
} else {
log.Printf("core.ordersout success: user=%s", o.Name)
}
}()
if err = json.Unmarshal(jsonIn, &o); err != nil {
return jsonStatusError(err)
}
if err := validateUser(o.Name, o.Password, false); err != nil {
return jsonStatusError(err)
}
out, err = json.Marshal(orders.Orders)
if err != nil {
return jsonStatusError(err)
}
return jsonResponse(out)
}
// OrderInfo will return a list of currently outstanding order numbers.
func OrderInfo(jsonIn []byte) (out []byte, err error) {
var o OrderInfoRequest
defer func() {
if err != nil {
log.Printf("core.order failed: user=%s %v", o.Name, err)
} else {
log.Printf("core.order success: user=%s", o.Name)
}
}()
if err = json.Unmarshal(jsonIn, &o); err != nil {
return jsonStatusError(err)
}
if err := validateUser(o.Name, o.Password, false); err != nil {
return jsonStatusError(err)
}
if ord, ok := orders.Orders[o.OrderNum]; ok {
if out, err = json.Marshal(ord); err != nil {
return jsonStatusError(err)
} else if len(out) == 0 {
return jsonStatusError(errors.New("No order with that number"))
}
return jsonResponse(out)
}
return jsonStatusError(errors.New("No order with that number"))
}
// OrderCancel will cancel an order given an order num
func OrderCancel(jsonIn []byte) (out []byte, err error) {
var o OrderCancelRequest
defer func() {
if err != nil {
log.Printf("core.order failed: user=%s %v", o.Name, err)
} else {
log.Printf("core.order success: user=%s", o.Name)
}
}()
if err = json.Unmarshal(jsonIn, &o); err != nil {
return jsonStatusError(err)
}
if err := validateUser(o.Name, o.Password, false); err != nil {
return jsonStatusError(err)
}
if ord, ok := orders.Orders[o.OrderNum]; ok {
if o.Name == ord.Creator {
delete(orders.Orders, o.OrderNum)
out = []byte("Successfully removed order")
return jsonResponse(out)
}
}
err = errors.New("Invalid Order Number")
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)
}
st := crypt.Status()
resp := &StatusData{Status: st.State}
if out, err = json.Marshal(resp); err != nil {
return jsonStatusError(err)
}
return jsonResponse(out)
}
// Restore attempts a restoration of the persistence store.
func Restore(jsonIn []byte) (out []byte, err error) {
var req DelegateRequest
var tags = map[string]string{"function": "core.Restore"}
defer func() {
if err != nil {
tags["restore.user"] = req.Name
report.Check(err, tags)
log.Printf("core.restore failed: user=%s %v", req.Name, err)
} else {
log.Printf("core.restore 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)
}
err = crypt.Restore(req.Name, req.Password, 1, "", req.Time)
if err != nil && err != cryptor.ErrRestoreDelegations {
return jsonStatusError(err)
}
st := crypt.Status()
resp := &StatusData{Status: st.State}
if out, err = json.Marshal(resp); err != nil {
return jsonStatusError(err)
}
return jsonResponse(out)
}
// ResetPersisted clears the persisted user data from the server. This
// request requires an admin.
func ResetPersisted(jsonIn []byte) (out []byte, err error) {
var req PurgeRequest
var tags = map[string]string{"function": "core.ResetPersisted"}
defer func() {
if err != nil {
tags["reset-persisted.user"] = req.Name
report.Check(err, tags)
log.Printf("core.resetpersisted failed: user=%s %v", req.Name, err)
} else {
log.Printf("core.resetpersisted success: user=%s", req.Name)
}
}()
if err = json.Unmarshal(jsonIn, &req); err != nil {
return jsonStatusError(err)
}
if err := validateUser(req.Name, req.Password, true); err != nil {
return jsonStatusError(err)
}
st, err := crypt.ResetPersisted()
if err != nil {
return jsonStatusError(err)
}
resp := &StatusData{Status: st.State}
if out, err = json.Marshal(resp); err != nil {
return jsonStatusError(err)
}
return jsonResponse(out)
}