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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
|
set testmodule [file normalize tests/modules/auth.so]
set testmoduletwo [file normalize tests/modules/moduleauthtwo.so]
set miscmodule [file normalize tests/modules/misc.so]
proc cmdstat {cmd} {
return [cmdrstat $cmd r]
}
start_server {tags {"modules"}} {
r module load $testmodule
r module load $testmoduletwo
set hello2_response [r HELLO 2]
set hello3_response [r HELLO 3]
test {test registering module auth callbacks} {
assert_equal {OK} [r testmoduleone.rm_register_blocking_auth_cb]
assert_equal {OK} [r testmoduletwo.rm_register_auth_cb]
assert_equal {OK} [r testmoduleone.rm_register_auth_cb]
}
test {test module AUTH for non existing / disabled users} {
r config resetstat
# Validate that an error is thrown for non existing users.
assert_error {*WRONGPASS*} {r AUTH foo pwd}
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
# Validate that an error is thrown for disabled users.
r acl setuser foo >pwd off ~* &* +@all
assert_error {*WRONGPASS*} {r AUTH foo pwd}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdstat auth]
}
test {test non blocking module AUTH} {
r config resetstat
# Test for a fixed password user
r acl setuser foo >pwd on ~* &* +@all
assert_equal {OK} [r AUTH foo allow]
assert_error {*Auth denied by Misc Module*} {r AUTH foo deny}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
assert_error {*WRONGPASS*} {r AUTH foo nomatch}
assert_match {*calls=3,*,rejected_calls=0,failed_calls=2} [cmdstat auth]
assert_equal {OK} [r AUTH foo pwd]
# Test for No Pass user
r acl setuser foo on ~* &* +@all nopass
assert_equal {OK} [r AUTH foo allow]
assert_error {*Auth denied by Misc Module*} {r AUTH foo deny}
assert_match {*calls=6,*,rejected_calls=0,failed_calls=3} [cmdstat auth]
assert_equal {OK} [r AUTH foo nomatch]
# Validate that the Module added an ACL Log entry.
set entry [lindex [r ACL LOG] 0]
assert {[dict get $entry username] eq {foo}}
assert {[dict get $entry context] eq {module}}
assert {[dict get $entry reason] eq {auth}}
assert {[dict get $entry object] eq {Module Auth}}
assert_match {*cmd=auth*} [dict get $entry client-info]
r ACL LOG RESET
}
test {test non blocking module HELLO AUTH} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
# Validate proto 2 and 3 in case of success
assert_equal $hello2_response [r HELLO 2 AUTH foo pwd]
assert_equal $hello2_response [r HELLO 2 AUTH foo allow]
assert_equal $hello3_response [r HELLO 3 AUTH foo pwd]
assert_equal $hello3_response [r HELLO 3 AUTH foo allow]
# Validate denying AUTH for the HELLO cmd
assert_error {*Auth denied by Misc Module*} {r HELLO 2 AUTH foo deny}
assert_match {*calls=5,*,rejected_calls=0,failed_calls=1} [cmdstat hello]
assert_error {*WRONGPASS*} {r HELLO 2 AUTH foo nomatch}
assert_match {*calls=6,*,rejected_calls=0,failed_calls=2} [cmdstat hello]
assert_error {*Auth denied by Misc Module*} {r HELLO 3 AUTH foo deny}
assert_match {*calls=7,*,rejected_calls=0,failed_calls=3} [cmdstat hello]
assert_error {*WRONGPASS*} {r HELLO 3 AUTH foo nomatch}
assert_match {*calls=8,*,rejected_calls=0,failed_calls=4} [cmdstat hello]
# Validate that the Module added an ACL Log entry.
set entry [lindex [r ACL LOG] 1]
assert {[dict get $entry username] eq {foo}}
assert {[dict get $entry context] eq {module}}
assert {[dict get $entry reason] eq {auth}}
assert {[dict get $entry object] eq {Module Auth}}
assert_match {*cmd=hello*} [dict get $entry client-info]
r ACL LOG RESET
}
test {test non blocking module HELLO AUTH SETNAME} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
# Validate clientname is set on success
assert_equal $hello2_response [r HELLO 2 AUTH foo pwd setname client1]
assert {[r client getname] eq {client1}}
assert_equal $hello2_response [r HELLO 2 AUTH foo allow setname client2]
assert {[r client getname] eq {client2}}
# Validate clientname is not updated on failure
r client setname client0
assert_error {*Auth denied by Misc Module*} {r HELLO 2 AUTH foo deny setname client1}
assert {[r client getname] eq {client0}}
assert_match {*calls=3,*,rejected_calls=0,failed_calls=1} [cmdstat hello]
assert_error {*WRONGPASS*} {r HELLO 2 AUTH foo nomatch setname client2}
assert {[r client getname] eq {client0}}
assert_match {*calls=4,*,rejected_calls=0,failed_calls=2} [cmdstat hello]
}
test {test blocking module AUTH} {
r config resetstat
# Test for a fixed password user
r acl setuser foo >pwd on ~* &* +@all
assert_equal {OK} [r AUTH foo block_allow]
assert_error {*Auth denied by Misc Module*} {r AUTH foo block_deny}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
assert_error {*WRONGPASS*} {r AUTH foo nomatch}
assert_match {*calls=3,*,rejected_calls=0,failed_calls=2} [cmdstat auth]
assert_equal {OK} [r AUTH foo pwd]
# Test for No Pass user
r acl setuser foo on ~* &* +@all nopass
assert_equal {OK} [r AUTH foo block_allow]
assert_error {*Auth denied by Misc Module*} {r AUTH foo block_deny}
assert_match {*calls=6,*,rejected_calls=0,failed_calls=3} [cmdstat auth]
assert_equal {OK} [r AUTH foo nomatch]
# Validate that every Blocking AUTH command took at least 500000 usec.
set stats [cmdstat auth]
regexp "usec_per_call=(\[0-9]{1,})\.*," $stats all usec_per_call
assert {$usec_per_call >= 500000}
# Validate that the Module added an ACL Log entry.
set entry [lindex [r ACL LOG] 0]
assert {[dict get $entry username] eq {foo}}
assert {[dict get $entry context] eq {module}}
assert {[dict get $entry reason] eq {auth}}
assert {[dict get $entry object] eq {Module Auth}}
assert_match {*cmd=auth*} [dict get $entry client-info]
r ACL LOG RESET
}
test {test blocking module HELLO AUTH} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
# validate proto 2 and 3 in case of success
assert_equal $hello2_response [r HELLO 2 AUTH foo pwd]
assert_equal $hello2_response [r HELLO 2 AUTH foo block_allow]
assert_equal $hello3_response [r HELLO 3 AUTH foo pwd]
assert_equal $hello3_response [r HELLO 3 AUTH foo block_allow]
# validate denying AUTH for the HELLO cmd
assert_error {*Auth denied by Misc Module*} {r HELLO 2 AUTH foo block_deny}
assert_match {*calls=5,*,rejected_calls=0,failed_calls=1} [cmdstat hello]
assert_error {*WRONGPASS*} {r HELLO 2 AUTH foo nomatch}
assert_match {*calls=6,*,rejected_calls=0,failed_calls=2} [cmdstat hello]
assert_error {*Auth denied by Misc Module*} {r HELLO 3 AUTH foo block_deny}
assert_match {*calls=7,*,rejected_calls=0,failed_calls=3} [cmdstat hello]
assert_error {*WRONGPASS*} {r HELLO 3 AUTH foo nomatch}
assert_match {*calls=8,*,rejected_calls=0,failed_calls=4} [cmdstat hello]
# Validate that every HELLO AUTH command took at least 500000 usec.
set stats [cmdstat hello]
regexp "usec_per_call=(\[0-9]{1,})\.*," $stats all usec_per_call
assert {$usec_per_call >= 500000}
# Validate that the Module added an ACL Log entry.
set entry [lindex [r ACL LOG] 1]
assert {[dict get $entry username] eq {foo}}
assert {[dict get $entry context] eq {module}}
assert {[dict get $entry reason] eq {auth}}
assert {[dict get $entry object] eq {Module Auth}}
assert_match {*cmd=hello*} [dict get $entry client-info]
r ACL LOG RESET
}
test {test blocking module HELLO AUTH SETNAME} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
# Validate clientname is set on success
assert_equal $hello2_response [r HELLO 2 AUTH foo pwd setname client1]
assert {[r client getname] eq {client1}}
assert_equal $hello2_response [r HELLO 2 AUTH foo block_allow setname client2]
assert {[r client getname] eq {client2}}
# Validate clientname is not updated on failure
r client setname client0
assert_error {*Auth denied by Misc Module*} {r HELLO 2 AUTH foo block_deny setname client1}
assert {[r client getname] eq {client0}}
assert_match {*calls=3,*,rejected_calls=0,failed_calls=1} [cmdstat hello]
assert_error {*WRONGPASS*} {r HELLO 2 AUTH foo nomatch setname client2}
assert {[r client getname] eq {client0}}
assert_match {*calls=4,*,rejected_calls=0,failed_calls=2} [cmdstat hello]
# Validate that every HELLO AUTH SETNAME command took at least 500000 usec.
set stats [cmdstat hello]
regexp "usec_per_call=(\[0-9]{1,})\.*," $stats all usec_per_call
assert {$usec_per_call >= 500000}
}
test {test AUTH after registering multiple module auth callbacks} {
r config resetstat
# Register two more callbacks from the same module.
assert_equal {OK} [r testmoduleone.rm_register_blocking_auth_cb]
assert_equal {OK} [r testmoduleone.rm_register_auth_cb]
# Register another module auth callback from the second module.
assert_equal {OK} [r testmoduletwo.rm_register_auth_cb]
r acl setuser foo >pwd on ~* &* +@all
# Case 1 - Non Blocking Success
assert_equal {OK} [r AUTH foo allow]
# Case 2 - Non Blocking Deny
assert_error {*Auth denied by Misc Module*} {r AUTH foo deny}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
r config resetstat
# Case 3 - Blocking Success
assert_equal {OK} [r AUTH foo block_allow]
# Case 4 - Blocking Deny
assert_error {*Auth denied by Misc Module*} {r AUTH foo block_deny}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
# Validate that every Blocking AUTH command took at least 500000 usec.
set stats [cmdstat auth]
regexp "usec_per_call=(\[0-9]{1,})\.*," $stats all usec_per_call
assert {$usec_per_call >= 500000}
r config resetstat
# Case 5 - Non Blocking Success via the second module.
assert_equal {OK} [r AUTH foo allow_two]
# Case 6 - Non Blocking Deny via the second module.
assert_error {*Auth denied by Misc Module*} {r AUTH foo deny_two}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
r config resetstat
# Case 7 - All four auth callbacks "Skip" by not explicitly allowing or denying.
assert_error {*WRONGPASS*} {r AUTH foo nomatch}
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
assert_equal {OK} [r AUTH foo pwd]
# Because we had to attempt all 4 callbacks, validate that the AUTH command took at least
# 1000000 usec (each blocking callback takes 500000 usec).
set stats [cmdstat auth]
regexp "usec_per_call=(\[0-9]{1,})\.*," $stats all usec_per_call
assert {$usec_per_call >= 1000000}
}
test {module auth during blocking module auth} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
set rd [redis_deferring_client]
set rd_two [redis_deferring_client]
# Attempt blocking module auth. While this ongoing, attempt non blocking module auth from
# moduleone/moduletwo and start another blocking module auth from another deferring client.
$rd AUTH foo block_allow
wait_for_blocked_clients_count 1
assert_equal {OK} [r AUTH foo allow]
assert_equal {OK} [r AUTH foo allow_two]
# Validate that the non blocking module auth cmds finished before any blocking module auth.
set info_clients [r info clients]
assert_match "*blocked_clients:1*" $info_clients
$rd_two AUTH foo block_allow
# Validate that all of the AUTH commands succeeded.
wait_for_blocked_clients_count 0 500 10
$rd flush
assert_equal [$rd read] "OK"
$rd_two flush
assert_equal [$rd_two read] "OK"
assert_match {*calls=4,*,rejected_calls=0,failed_calls=0} [cmdstat auth]
}
test {module auth inside MULTI EXEC} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
# Validate that non blocking module auth inside MULTI succeeds.
r multi
r AUTH foo allow
assert_equal {OK} [r exec]
# Validate that blocking module auth inside MULTI throws an err.
r multi
r AUTH foo block_allow
assert_error {*ERR Blocking module command called from transaction*} {r exec}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
}
test {Disabling Redis User during blocking module auth} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
set rd [redis_deferring_client]
# Attempt blocking module auth and disable the Redis user while module auth is in progress.
$rd AUTH foo pwd
wait_for_blocked_clients_count 1
r acl setuser foo >pwd off ~* &* +@all
# Validate that module auth failed.
wait_for_blocked_clients_count 0 500 10
$rd flush
assert_error {*WRONGPASS*} { $rd read }
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
}
test {Killing a client in the middle of blocking module auth} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
set rd [redis_deferring_client]
$rd client id
set cid [$rd read]
# Attempt blocking module auth command on client `cid` and kill the client while module auth
# is in progress.
$rd AUTH foo pwd
wait_for_blocked_clients_count 1
r client kill id $cid
# Validate that the blocked client count goes to 0 and no AUTH command is tracked.
wait_for_blocked_clients_count 0 500 10
$rd flush
assert_error {*I/O error reading reply*} { $rd read }
assert_match {} [cmdstat auth]
}
test {test RM_AbortBlock Module API during blocking module auth} {
r config resetstat
r acl setuser foo >pwd on ~* &* +@all
# Attempt module auth. With the "block_abort" as the password, the "testacl.so" module
# blocks the client and uses the RM_AbortBlock API. This should result in module auth
# failing and the client being unblocked with the default AUTH err message.
assert_error {*WRONGPASS*} {r AUTH foo block_abort}
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdstat auth]
}
test {test RM_RegisterAuthCallback Module API during blocking module auth} {
r config resetstat
r acl setuser foo >defaultpwd on ~* &* +@all
set rd [redis_deferring_client]
# Start the module auth attempt with the standard Redis auth password for the user. This
# will result in all module auth cbs attempted and then standard Redis auth will be tried.
$rd AUTH foo defaultpwd
wait_for_blocked_clients_count 1
# Validate that we allow modules to register module auth cbs while module auth is already
# in progress.
assert_equal {OK} [r testmoduleone.rm_register_blocking_auth_cb]
assert_equal {OK} [r testmoduletwo.rm_register_auth_cb]
# Validate that blocking module auth succeeds.
wait_for_blocked_clients_count 0 500 10
$rd flush
assert_equal [$rd read] "OK"
set stats [cmdstat auth]
assert_match {*calls=1,*,rejected_calls=0,failed_calls=0} $stats
# Validate that even the new blocking module auth cb which was registered in the middle of
# blocking module auth is attempted - making it take twice the duration (2x 500000 us).
regexp "usec_per_call=(\[0-9]{1,})\.*," $stats all usec_per_call
assert {$usec_per_call >= 1000000}
}
test {Module unload during blocking module auth} {
r config resetstat
r module load $miscmodule
set rd [redis_deferring_client]
r acl setuser foo >pwd on ~* &* +@all
# Start a blocking module auth attempt.
$rd AUTH foo block_allow
wait_for_blocked_clients_count 1
# moduleone and moduletwo have module auth cbs registered. Because blocking module auth is
# ongoing, they cannot be unloaded.
catch {r module unload testacl} e
assert_match {*the module has blocked clients*} $e
# The moduleauthtwo module can be unregistered because no client is blocked on it.
assert_equal "OK" [r module unload moduleauthtwo]
# The misc module does not have module auth cbs registered, so it can be unloaded even when
# blocking module auth is ongoing.
assert_equal "OK" [r module unload misc]
# Validate that blocking module auth succeeds.
wait_for_blocked_clients_count 0 500 10
$rd flush
assert_equal [$rd read] "OK"
assert_match {*calls=1,*,rejected_calls=0,failed_calls=0} [cmdstat auth]
# Validate that unloading the moduleauthtwo module does not unregister module auth cbs of
# of the testacl module. Module based auth should succeed.
assert_equal {OK} [r AUTH foo allow]
# Validate that the testacl module can be unloaded since blocking module auth is done.
r module unload testacl
# Validate that since all module auth cbs are unregistered, module auth attempts fail.
assert_error {*WRONGPASS*} {r AUTH foo block_allow}
assert_error {*WRONGPASS*} {r AUTH foo allow_two}
assert_error {*WRONGPASS*} {r AUTH foo allow}
assert_match {*calls=5,*,rejected_calls=0,failed_calls=3} [cmdstat auth]
}
}
|