-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
handler_janitor.go
187 lines (149 loc) · 5.33 KB
/
handler_janitor.go
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package cli
import (
"context"
"fmt"
"time"
"github.com/ory/hydra/persistence"
"github.com/pkg/errors"
"github.com/ory/x/flagx"
"github.com/spf13/cobra"
"github.com/ory/hydra/driver"
"github.com/ory/hydra/driver/config"
"github.com/ory/x/configx"
"github.com/ory/x/errorsx"
)
const (
Limit = "limit"
BatchSize = "batch-size"
KeepIfYounger = "keep-if-younger"
AccessLifespan = "access-lifespan"
RefreshLifespan = "refresh-lifespan"
ConsentRequestLifespan = "consent-request-lifespan"
OnlyTokens = "tokens"
OnlyRequests = "requests"
OnlyGrants = "grants"
ReadFromEnv = "read-from-env"
Config = "config"
)
type JanitorHandler struct{}
func NewJanitorHandler() *JanitorHandler {
return &JanitorHandler{}
}
func (_ *JanitorHandler) Args(cmd *cobra.Command, args []string) error {
if len(args) == 0 &&
!flagx.MustGetBool(cmd, ReadFromEnv) &&
len(flagx.MustGetStringSlice(cmd, Config)) == 0 {
fmt.Printf("%s\n", cmd.UsageString())
return fmt.Errorf("%s\n%s\n%s\n",
"A DSN is required as a positional argument when not passing any of the following flags:",
"- Using the environment variable with flag -e, --read-from-env",
"- Using the config file with flag -c, --config")
}
if !flagx.MustGetBool(cmd, OnlyTokens) && !flagx.MustGetBool(cmd, OnlyRequests) && !flagx.MustGetBool(cmd, OnlyGrants) {
return fmt.Errorf("%s\n%s\n", cmd.UsageString(),
"Janitor requires at least one of --tokens, --requests or --grants to be set")
}
limit := flagx.MustGetInt(cmd, Limit)
batchSize := flagx.MustGetInt(cmd, BatchSize)
if limit <= 0 || batchSize <= 0 {
return fmt.Errorf("%s\n%s\n", cmd.UsageString(),
"Values for --limit and --batch-size should both be greater than 0")
}
if batchSize > limit {
return fmt.Errorf("%s\n%s\n", cmd.UsageString(),
"Value for --batch-size must not be greater than value for --limit")
}
return nil
}
func (_ *JanitorHandler) RunE(cmd *cobra.Command, args []string) error {
return purge(cmd, args)
}
func purge(cmd *cobra.Command, args []string) error {
var d driver.Registry
co := []configx.OptionModifier{
configx.WithFlags(cmd.Flags()),
configx.SkipValidation(),
}
keys := map[string]string{
AccessLifespan: config.KeyAccessTokenLifespan,
RefreshLifespan: config.KeyRefreshTokenLifespan,
ConsentRequestLifespan: config.KeyConsentRequestMaxAge,
}
for k, v := range keys {
if x := flagx.MustGetDuration(cmd, k); x > 0 {
co = append(co, configx.WithValue(v, x))
}
}
notAfter := time.Now()
if keepYounger := flagx.MustGetDuration(cmd, KeepIfYounger); keepYounger > 0 {
notAfter = notAfter.Add(-keepYounger)
}
if !flagx.MustGetBool(cmd, ReadFromEnv) && len(flagx.MustGetStringSlice(cmd, Config)) == 0 {
co = append(co, configx.WithValue(config.KeyDSN, args[0]))
}
do := []driver.OptionsModifier{
driver.DisableValidation(),
driver.DisablePreloading(),
driver.WithOptions(co...),
}
d = driver.New(cmd.Context(), do...)
if len(d.Config().DSN()) == 0 {
return fmt.Errorf("%s\n%s\n%s\n", cmd.UsageString(),
"When using flag -e, environment variable DSN must be set.",
"When using flag -c, the dsn property should be set.")
}
if err := d.Init(cmd.Context()); err != nil {
return fmt.Errorf("%s\n%s\n", cmd.UsageString(),
"Janitor can only be executed against a SQL-compatible driver but DSN is not a SQL source.")
}
p := d.Persister()
limit := flagx.MustGetInt(cmd, Limit)
batchSize := flagx.MustGetInt(cmd, BatchSize)
var routineFlags []string
if flagx.MustGetBool(cmd, OnlyTokens) {
routineFlags = append(routineFlags, OnlyTokens)
}
if flagx.MustGetBool(cmd, OnlyRequests) {
routineFlags = append(routineFlags, OnlyRequests)
}
if flagx.MustGetBool(cmd, OnlyGrants) {
routineFlags = append(routineFlags, OnlyGrants)
}
return cleanupRun(cmd.Context(), notAfter, limit, batchSize, addRoutine(p, routineFlags...)...)
}
func addRoutine(p persistence.Persister, names ...string) []cleanupRoutine {
var routines []cleanupRoutine
for _, n := range names {
switch n {
case OnlyTokens:
routines = append(routines, cleanup(p.FlushInactiveAccessTokens, "access tokens"))
routines = append(routines, cleanup(p.FlushInactiveRefreshTokens, "refresh tokens"))
case OnlyRequests:
routines = append(routines, cleanup(p.FlushInactiveLoginConsentRequests, "login-consent requests"))
case OnlyGrants:
routines = append(routines, cleanup(p.FlushInactiveGrants, "grants"))
}
}
return routines
}
type cleanupRoutine func(ctx context.Context, notAfter time.Time, limit int, batchSize int) error
func cleanup(cr cleanupRoutine, routineName string) cleanupRoutine {
return func(ctx context.Context, notAfter time.Time, limit int, batchSize int) error {
if err := cr(ctx, notAfter, limit, batchSize); err != nil {
return errors.Wrap(errorsx.WithStack(err), fmt.Sprintf("Could not cleanup inactive %s", routineName))
}
fmt.Printf("Successfully completed Janitor run on %s\n", routineName)
return nil
}
}
func cleanupRun(ctx context.Context, notAfter time.Time, limit int, batchSize int, routines ...cleanupRoutine) error {
if len(routines) == 0 {
return errors.New("clean up run received 0 routines")
}
for _, r := range routines {
if err := r(ctx, notAfter, limit, batchSize); err != nil {
return err
}
}
return nil
}