@@ -27,6 +27,7 @@ component accessors="true" singleton{
27
27
property name = " cachebox" inject = " cachebox" ;
28
28
property name = " settings" inject = " coldbox:moduleSettings:cbSecurity" ;
29
29
property name = " jwtService" inject = " JwtService@cbSecurity" ;
30
+ property name = " log" inject = " logbox:logger:{this}" ;
30
31
31
32
/**
32
33
* Storage properties
@@ -81,12 +82,74 @@ component accessors="true" singleton{
81
82
if ( isNull ( variables .properties .schema ) ){
82
83
variables .properties .schema = " " ;
83
84
}
84
-
85
+ // Days since expiration of token, to remove
86
+ if ( isNull ( variables .properties .rotationDays ) ){
87
+ variables .properties .rotationDays = 7 ;
88
+ }
89
+ // Run rotations every hour by default
90
+ if ( isNull ( variables .properties .rotationFrequency ) ){
91
+ variables .properties .rotationFrequency = 60 ;
92
+ }
85
93
// Build out table
86
94
if ( variables .properties .autoCreate ){
87
95
ensureTable ();
88
96
}
89
97
98
+ // DB Rotation Time
99
+ variables .lastDBRotation = " " ;
100
+
101
+ return this ;
102
+ }
103
+
104
+ /**
105
+ * Rotation checks
106
+ */
107
+ function rotationCheck (){
108
+ // Verify if in rotation frequency
109
+ if (
110
+ isDate ( variables .lastDBRotation )
111
+ AND
112
+ dateDiff ( " n" , variables .lastDBRotation , now () ) LTE variables .properties .rotationFrequency
113
+ ){
114
+ return ;
115
+ }
116
+
117
+ // Rotations
118
+ this .doRotation ();
119
+ }
120
+
121
+ /**
122
+ * Do the rotation
123
+ */
124
+ function doRotation (){
125
+ var qLogs = " " ;
126
+ var cols = variables .columns ;
127
+ var targetDate = dateAdd ( " d" , " -#variables .properties .rotationDays #" , now () );
128
+
129
+ if ( variables .log .canInfo () ){
130
+ variables .log .info ( " DBTokenStorage starting token rotation using (#variables .properties .rotationDays #) rotation days" );
131
+ }
132
+
133
+ var qResults = queryExecute (
134
+ " DELETE
135
+ FROM #getTable () #
136
+ WHERE expiration < :targetDate
137
+ " ,
138
+ {
139
+ targetDate = { cfsqltype = " timestamp" , value = targetDate }
140
+ },
141
+ {
142
+ datasource = variables .properties .dsn
143
+ }
144
+ );
145
+
146
+ // Store last profile time
147
+ variables .lastDBRotation = now ();
148
+
149
+ if ( variables .log .canInfo () ){
150
+ variables .log .info ( " DBTokenStorage finalized rotation" , qResults );
151
+ }
152
+
90
153
return this ;
91
154
}
92
155
@@ -122,26 +185,39 @@ component accessors="true" singleton{
122
185
cacheKey = { cfsqltype = " varchar" , value = arguments .key },
123
186
token = { cfsqltype = " longvarchar" ,value = arguments .token },
124
187
expiration = { cfsqltype = " timestamp" , value = jwtService .fromEpoch ( arguments .payload .exp ) },
125
- issued = { cfsqltype = " timestamp" , value = jwtService .fromEpoch ( arguments .payload .iat ) },
188
+ issued = { cfsqltype = " timestamp" , value = jwtService .fromEpoch ( arguments .payload .iat ) },
126
189
subject = { cfsqltype = " varchar" , value = arguments .payload .sub },
127
190
},
128
191
{
129
192
datasource = variables .properties .dsn
130
193
}
131
194
);
195
+
196
+ // Run rotation checks
197
+ rotationCheck ();
198
+
132
199
return this ;
133
200
}
134
201
135
202
/**
136
- * Verify if the passed in token key exists
203
+ * Verify if the passed in token key exists and is valid.
137
204
*
138
205
* @key The cache key
139
206
*/
140
207
boolean function exists ( required key ){
208
+ // Run rotation checks first!
209
+ rotationCheck ();
210
+
211
+ // Verify now
141
212
var qResults = queryExecute (
142
- " SELECT cacheKey FROM #getTable () # WHERE cacheKey = :cacheKey" ,
213
+ " SELECT cacheKey
214
+ FROM #getTable () #
215
+ WHERE cacheKey = :cacheKey
216
+ AND expiration >= :now
217
+ " ,
143
218
{
144
- cacheKey : arguments .key
219
+ cacheKey : arguments .key ,
220
+ now : { cfsqltype = " timestamp" , value = now () },
145
221
},
146
222
{
147
223
datsource = variables .properties .dsn
@@ -159,6 +235,9 @@ component accessors="true" singleton{
159
235
* @throws TokenNotFoundException
160
236
*/
161
237
struct function get ( required key , struct defaultValue ){
238
+ // Run rotation checks first
239
+ rotationCheck ();
240
+
162
241
// select entry
163
242
var q = queryExecute (
164
243
" SELECT cacheKey, token, expiration, issued
@@ -195,6 +274,9 @@ component accessors="true" singleton{
195
274
* @return JWTStorage
196
275
*/
197
276
any function clear ( required any key ){
277
+ // Run rotation checks
278
+ rotationCheck ();
279
+
198
280
queryExecute (
199
281
" DELETE
200
282
FROM #getTable () #
0 commit comments