-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommands_dumplog.go
More file actions
125 lines (105 loc) · 2.92 KB
/
commands_dumplog.go
File metadata and controls
125 lines (105 loc) · 2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package main
import (
"fmt"
"path"
"regexp"
"strconv"
"strings"
"time"
types "github.com/distributeddesigns/shared_types"
"github.com/streadway/amqp"
)
type dumplogCmd struct {
id uint64
userID string
filename string
}
func parseDumplogCmd(parts []string) dumplogCmd {
if len(parts) < 3 {
abortTx("DUMPLOG needs at least 3 parts")
}
id, err := strconv.ParseUint(parts[0], 10, 64)
abortTxOnError(err, "Could not parse ID")
// Dumplog is overloaded as
// 1) DUMPLOG,$filename
// 2) DUMPLOG,$userID,$filename
// The first case is an admin (global) dump
var userID, filename string
if len(parts) == 3 {
userID = "admin"
filename = parts[2]
} else {
userID = parts[2]
filename = parts[3]
}
return dumplogCmd{id, userID, safeFileName(filename)}
}
func (dl dumplogCmd) Name() string {
return fmt.Sprintf("[%d] DUMPLOG", dl.id)
}
func (dl dumplogCmd) ToAuditEvent() types.AuditEvent {
// Workload file validator will fail if it sees the injected "admin" account
// in the workload file. We'll make it disappear here but carry it through
// the rest of the system.
var userNameField string
if dl.userID != "admin" {
userNameField = fmt.Sprintf("\n\t\t<username>%s</username>", dl.userID)
}
xmlElement := fmt.Sprintf(`
<userCommand>
<timestamp>%d</timestamp>
<server>%s</server>
<transactionNum>%d</transactionNum>
<command>DUMPLOG</command>%s
<filename>%s</filename>
</userCommand>`,
time.Now().UnixNano()/1e6, redisBaseKey, dl.id, userNameField, dl.filename,
)
return types.AuditEvent{
UserID: dl.userID,
ID: dl.id,
EventType: "command",
Content: xmlElement,
}
}
func (dl dumplogCmd) Execute() {
abortTxIfNoAccount(dl.userID)
dlr := types.DumplogRequest{
UserID: dl.userID,
Filename: dl.filename,
}
// Optimistically send request. It's up to the user to retrieve the file~
ch, err := rmqConn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.Publish(
"", // exchange
dumplogQ, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/csv",
Body: []byte(dlr.ToCSV()),
})
failOnError(err, "Failed to publish a message")
consoleLog.Debug("Dumplog requested as", dlr.Filename)
acct := accountStore[dl.userID]
acct.AddSummaryItem("Finished " + dl.Name())
consoleLog.Notice(" [✔] Finished", dl.Name())
}
// Compile the file sanitization regexps once and only once
var (
separators = regexp.MustCompile(`[ &_=+:]`)
legal = regexp.MustCompile(`[^[:alnum:]-.]`)
)
// Convert to a string that's suitable for use as a filename.
// Lifted from asaskevich/govalidator
func safeFileName(str string) string {
name := strings.ToLower(str)
name = path.Clean(path.Base(name)) // "./foo/bar" -> "bar"
name = strings.Trim(name, " ")
name = separators.ReplaceAllString(name, "-")
name = legal.ReplaceAllString(name, "")
name = strings.Replace(name, "--", "-", -1)
return name
}