Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
034dcf18
Commit
034dcf18
authored
Feb 26, 2019
by
Madelyn Olson
Browse files
Add module APIs for custom authentication
parent
e9b99c78
Changes
13
Hide whitespace changes
Inline
Side-by-side
runtest-moduleapi
View file @
034dcf18
...
@@ -24,4 +24,5 @@ $TCLSH tests/test_helper.tcl \
...
@@ -24,4 +24,5 @@ $TCLSH tests/test_helper.tcl \
--single
unit/moduleapi/blockonkeys
\
--single
unit/moduleapi/blockonkeys
\
--single
unit/moduleapi/scan
\
--single
unit/moduleapi/scan
\
--single
unit/moduleapi/datatype
\
--single
unit/moduleapi/datatype
\
--single
unit/moduleapi/auth
\
"
${
@
}
"
"
${
@
}
"
src/acl.c
View file @
034dcf18
...
@@ -975,6 +975,7 @@ int ACLAuthenticateUser(client *c, robj *username, robj *password) {
...
@@ -975,6 +975,7 @@ int ACLAuthenticateUser(client *c, robj *username, robj *password) {
if
(
ACLCheckUserCredentials
(
username
,
password
)
==
C_OK
)
{
if
(
ACLCheckUserCredentials
(
username
,
password
)
==
C_OK
)
{
c
->
authenticated
=
1
;
c
->
authenticated
=
1
;
c
->
user
=
ACLGetUserByName
(
username
->
ptr
,
sdslen
(
username
->
ptr
));
c
->
user
=
ACLGetUserByName
(
username
->
ptr
,
sdslen
(
username
->
ptr
));
moduleNotifyUserChanged
(
c
);
return
C_OK
;
return
C_OK
;
}
else
{
}
else
{
return
C_ERR
;
return
C_ERR
;
...
...
src/module.c
View file @
034dcf18
...
@@ -355,6 +355,21 @@ list *RedisModule_EventListeners; /* Global list of all the active events. */
...
@@ -355,6 +355,21 @@ list *RedisModule_EventListeners; /* Global list of all the active events. */
unsigned
long
long
ModulesInHooks
=
0
;
/* Total number of modules in hooks
unsigned
long
long
ModulesInHooks
=
0
;
/* Total number of modules in hooks
callbacks right now. */
callbacks right now. */
/* Data structures related to the redis module users */
/* This callback type is called by moduleNotifyUserChanged() every time
* a user authenticated via the module API is associated with a different
* user or gets disconnected. */
typedef
void
(
*
RedisModuleUserChangedFunc
)
(
uint64_t
client_id
,
void
*
privdata
);
/* This is the object returned by RM_CreateModuleUser(). The module API is
* able to create users, set ACLs to such users, and later authenticate
* clients using such newly created users. */
typedef
struct
RedisModuleUser
{
user
*
user
;
/* Reference to the real redis user */
}
RedisModuleUser
;
/* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* Prototypes
* Prototypes
* -------------------------------------------------------------------------- */
* -------------------------------------------------------------------------- */
...
@@ -719,6 +734,7 @@ int commandFlagsFromString(char *s) {
...
@@ -719,6 +734,7 @@ int commandFlagsFromString(char *s) {
else
if
(
!
strcasecmp
(
t
,
"allow-stale"
))
flags
|=
CMD_STALE
;
else
if
(
!
strcasecmp
(
t
,
"allow-stale"
))
flags
|=
CMD_STALE
;
else
if
(
!
strcasecmp
(
t
,
"no-monitor"
))
flags
|=
CMD_SKIP_MONITOR
;
else
if
(
!
strcasecmp
(
t
,
"no-monitor"
))
flags
|=
CMD_SKIP_MONITOR
;
else
if
(
!
strcasecmp
(
t
,
"fast"
))
flags
|=
CMD_FAST
;
else
if
(
!
strcasecmp
(
t
,
"fast"
))
flags
|=
CMD_FAST
;
else
if
(
!
strcasecmp
(
t
,
"no-auth"
))
flags
|=
CMD_NO_AUTH
;
else
if
(
!
strcasecmp
(
t
,
"getkeys-api"
))
flags
|=
CMD_MODULE_GETKEYS
;
else
if
(
!
strcasecmp
(
t
,
"getkeys-api"
))
flags
|=
CMD_MODULE_GETKEYS
;
else
if
(
!
strcasecmp
(
t
,
"no-cluster"
))
flags
|=
CMD_MODULE_NO_CLUSTER
;
else
if
(
!
strcasecmp
(
t
,
"no-cluster"
))
flags
|=
CMD_MODULE_NO_CLUSTER
;
else
break
;
else
break
;
...
@@ -780,6 +796,9 @@ int commandFlagsFromString(char *s) {
...
@@ -780,6 +796,9 @@ int commandFlagsFromString(char *s) {
* example, is unable to report the position of the
* example, is unable to report the position of the
* keys, programmatically creates key names, or any
* keys, programmatically creates key names, or any
* other reason.
* other reason.
* * **"no-auth"**: This command can be run by an un-authenticated client.
* Normally this is used by a command that is used
* to authenticate a client.
*/
*/
int
RM_CreateCommand
(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
RedisModuleCmdFunc
cmdfunc
,
const
char
*
strflags
,
int
firstkey
,
int
lastkey
,
int
keystep
)
{
int
RM_CreateCommand
(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
RedisModuleCmdFunc
cmdfunc
,
const
char
*
strflags
,
int
firstkey
,
int
lastkey
,
int
keystep
)
{
int
flags
=
strflags
?
commandFlagsFromString
((
char
*
)
strflags
)
:
0
;
int
flags
=
strflags
?
commandFlagsFromString
((
char
*
)
strflags
)
:
0
;
...
@@ -5236,6 +5255,202 @@ int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remain
...
@@ -5236,6 +5255,202 @@ int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remain
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
/* --------------------------------------------------------------------------
* Modules ACL API
*
* Implements a hook into the authentication and authorization within Redis.
* --------------------------------------------------------------------------*/
/* This function is called when a client's user has changed and invoked a
* a modules client changed callback if it was set. This callback should
* cleanup any state the module was tracking about this client.
*
* A client's user can be changed through the AUTH command, module
* authentication, and when the client is freed. */
void
moduleNotifyUserChanged
(
client
*
c
)
{
if
(
c
->
auth_callback
)
{
c
->
auth_callback
(
c
->
id
,
c
->
auth_callback_privdata
);
/* The callback will fire exactly once, even if the user remains
* the same, it is expected to completely clean up it's state
* so all references are removed */
c
->
auth_callback
=
NULL
;
c
->
auth_callback_privdata
=
NULL
;
c
->
auth_module
=
NULL
;
}
}
void
revokeClientAuthentication
(
client
*
c
)
{
/* Fire the client changed handler now in case we are unloading the module
* and need to cleanup. */
moduleNotifyUserChanged
(
c
);
c
->
user
=
DefaultUser
;
c
->
authenticated
=
0
;
freeClientAsync
(
c
);
}
/* Cleanup all clients that have been authenticated with this module. This
* is called from onUnload() to give the module a chance to cleanup any
* resources associated with the authentication. */
static
void
moduleFreeAuthenticatedClients
(
RedisModule
*
module
)
{
listIter
li
;
listNode
*
ln
;
listRewind
(
server
.
clients
,
&
li
);
while
((
ln
=
listNext
(
&
li
))
!=
NULL
)
{
client
*
c
=
listNodeValue
(
ln
);
if
(
!
c
->
auth_module
)
continue
;
RedisModule
*
auth_module
=
(
RedisModule
*
)
c
->
auth_module
;
if
(
auth_module
==
module
)
{
revokeClientAuthentication
(
c
);
}
}
}
/* Creates a Redis ACL user that the module can use to authenticate a client.
* After obtaining the user, the module should set what such user can do
* using the RM_SetUserACL() function. Once configured, the user
* can be used in order to authenticate a connection, with the specified
* ACL rules, using the RedisModule_AuthClientWithUser() function.
*
* Note that:
*
* * Users created here are not listed by the ACL command.
* * Users created here are not checked for duplicated name, so it's up to
* the module calling this function to take care of not creating users
* with the same name.
* * The created user can be used to authenticate multiple Redis connections.
*
* The caller can later free the user using the function
* RM_FreeModuleUser(). When this function is called, if there are
* still clients authenticated with this user, they are disconnected.
* The function to free the user should only be used when the caller really
* wants to invalidate the user to define a new one with different
* capabilities. */
RedisModuleUser
*
RM_CreateModuleUser
(
const
char
*
name
)
{
RedisModuleUser
*
new_user
=
zmalloc
(
sizeof
(
RedisModuleUser
));
new_user
->
user
=
ACLCreateUnlinkedUser
();
/* Free the previous temporarily assigned name to assign the new one */
sdsfree
(
new_user
->
user
->
name
);
new_user
->
user
->
name
=
sdsnew
(
name
);
return
new_user
;
}
/* Frees a given user and disconnects all of the clients that have been
* authenticated with it. See RM_CreateModuleUser for detailed usage.*/
int
RM_FreeModuleUser
(
RedisModuleUser
*
user
)
{
ACLFreeUserAndKillClients
(
user
->
user
);
zfree
(
user
);
return
REDISMODULE_OK
;
}
/* Sets the permissions of a user created through the redis module
* interface. The syntax is the same as ACL SETUSER, so refer to the
* documentation in acl.c for more information. See RM_CreateModuleUser
* for detailed usage.
*
* Returns REDISMODULE_OK on success and REDISMODULE_ERR on failure
* and will set an errno describing why the operation failed. */
int
RM_SetModuleUserACL
(
RedisModuleUser
*
user
,
const
char
*
acl
)
{
return
ACLSetUser
(
user
->
user
,
acl
,
-
1
);
}
/* Authenticate the client associated with the context with
* the provided user. Returns REDISMODULE_OK on success and
* REDISMODULE_ERR on error.
*
* This authentication can be tracked with the optional callback and private
* data fields. The callback will be called whenever the user of the client
* changes. This callback should be used to cleanup any state that is being
* kept in the module related to the client authentication. It will only be
* called once, even when the user hasn't changed, in order to allow for a
* new callback to be specified. If this authentication does not need to be
* tracked, pass in NULL for the callback and privdata.
*
* If client_id is not NULL, it will be filled with the id of the client
* that was authenticated. This can be used with the
* RM_DeauthenticateAndCloseClient() API in order to deauthenticate a
* previously authenticated client if the authentication is no longer valid.
*
* For expensive authentication operations, it is recommended to block the
* client and do the authentication in the background then attach the user
* to the client in a threadsafe context. */
static
int
authenticateClientWithUser
(
RedisModuleCtx
*
ctx
,
user
*
user
,
RedisModuleUserChangedFunc
callback
,
void
*
privdata
,
uint64_t
*
client_id
)
{
if
(
user
->
flags
&
USER_FLAG_DISABLED
)
{
return
REDISMODULE_ERR
;
}
/* Freeing the client would result in moduleNotifyUserChanged() to be
* called later, however since we use revokeClientAuthentication() also
* in moduleFreeAuthenticatedClients() to implement module unloading, we
* do this action ASAP: this way if the module is unloaded, when the client
* is eventually freed we don't rely on the module to still exist. */
moduleNotifyUserChanged
(
ctx
->
client
);
ctx
->
client
->
user
=
user
;
ctx
->
client
->
authenticated
=
1
;
if
(
callback
)
{
ctx
->
client
->
auth_callback
=
callback
;
ctx
->
client
->
auth_callback_privdata
=
privdata
;
ctx
->
client
->
auth_module
=
ctx
->
module
;
}
if
(
client_id
)
{
*
client_id
=
ctx
->
client
->
id
;
}
return
REDISMODULE_OK
;
}
/* Authenticate the current context's user with the provided redis acl user.
* Returns REDISMODULE_ERR if the user is disabled.
*
* See authenticateClientWithUser for information about callback and client_id,
* and general usage for authentication. */
int
RM_AuthenticateClientWithUser
(
RedisModuleCtx
*
ctx
,
RedisModuleUser
*
module_user
,
RedisModuleUserChangedFunc
callback
,
void
*
privdata
,
uint64_t
*
client_id
)
{
return
authenticateClientWithUser
(
ctx
,
module_user
->
user
,
callback
,
privdata
,
client_id
);
}
/* Authenticate the current context's user with the provided redis acl user.
* Returns REDISMODULE_ERR if the user is disabled or the user does not exist.
*
* See authenticateClientWithUser for information about callback and client_id,
* and general usage for authentication. */
int
RM_AuthenticateClientWithACLUser
(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
size_t
len
,
RedisModuleUserChangedFunc
callback
,
void
*
privdata
,
uint64_t
*
client_id
)
{
user
*
acl_user
=
ACLGetUserByName
(
name
,
len
);
if
(
!
acl_user
)
{
return
REDISMODULE_ERR
;
}
return
authenticateClientWithUser
(
ctx
,
acl_user
,
callback
,
privdata
,
client_id
);
}
/* Deauthenticate and close the client. The client resources will not be
* be immediately freed, but will be cleaned up in a background job. This is
* the recommended way to deauthenicate a client since most clients can't
* handle users becomming deauthenticated. Returns REDISMODULE_ERR when the
* client doesn't exist and REDISMODULE_OK when the operation was successful.
*
* The client ID can be obtained from the AuthenticateClientWithUser and
* AuthenticateClientWithACLUser APIs or through other APIs such as
* server events.
*
* This function is not thread safe, and must be executed within the context
* of a command or thread safe context. */
int
RM_DeauthenticateAndCloseClient
(
RedisModuleCtx
*
ctx
,
uint64_t
client_id
)
{
UNUSED
(
ctx
);
client
*
c
=
lookupClientByID
(
client_id
);
if
(
c
==
NULL
)
return
REDISMODULE_ERR
;
/* Revoke also marks client to be closed ASAP */
revokeClientAuthentication
(
c
);
return
REDISMODULE_OK
;
}
/* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* Modules Dictionary API
* Modules Dictionary API
*
*
...
@@ -7078,6 +7293,7 @@ int moduleUnload(sds name) {
...
@@ -7078,6 +7293,7 @@ int moduleUnload(sds name) {
}
}
}
}
moduleFreeAuthenticatedClients
(
module
);
moduleUnregisterCommands
(
module
);
moduleUnregisterCommands
(
module
);
moduleUnregisterSharedAPI
(
module
);
moduleUnregisterSharedAPI
(
module
);
moduleUnregisterUsedAPI
(
module
);
moduleUnregisterUsedAPI
(
module
);
...
@@ -7561,4 +7777,10 @@ void moduleRegisterCoreAPI(void) {
...
@@ -7561,4 +7777,10 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API
(
ScanCursorRestart
);
REGISTER_API
(
ScanCursorRestart
);
REGISTER_API
(
Scan
);
REGISTER_API
(
Scan
);
REGISTER_API
(
ScanKey
);
REGISTER_API
(
ScanKey
);
REGISTER_API
(
CreateModuleUser
);
REGISTER_API
(
SetModuleUserACL
);
REGISTER_API
(
FreeModuleUser
);
REGISTER_API
(
DeauthenticateAndCloseClient
);
REGISTER_API
(
AuthenticateClientWithACLUser
);
REGISTER_API
(
AuthenticateClientWithUser
);
}
}
src/modules/Makefile
View file @
034dcf18
...
@@ -13,7 +13,7 @@ endif
...
@@ -13,7 +13,7 @@ endif
.SUFFIXES
:
.c .so .xo .o
.SUFFIXES
:
.c .so .xo .o
all
:
helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so hellohook.so
all
:
helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so hellohook.so
helloacl.so
.c.xo
:
.c.xo
:
$(CC)
-I
.
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
$(CC)
-I
.
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
...
@@ -53,6 +53,11 @@ hellohook.xo: ../redismodule.h
...
@@ -53,6 +53,11 @@ hellohook.xo: ../redismodule.h
hellohook.so
:
hellohook.xo
hellohook.so
:
hellohook.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
helloacl.xo
:
../redismodule.h
helloacl.so
:
helloacl.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
testmodule.xo
:
../redismodule.h
testmodule.xo
:
../redismodule.h
testmodule.so
:
testmodule.xo
testmodule.so
:
testmodule.xo
...
...
src/modules/helloacl.c
0 → 100644
View file @
034dcf18
/* ACL API example - An example of performing custom password authentication
*
* -----------------------------------------------------------------------------
*
* Copyright 2019 Amazon.com, Inc. or its affiliates.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define REDISMODULE_EXPERIMENTAL_API
#include "../redismodule.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <unistd.h>
// A simple global user
static
RedisModuleUser
*
global
;
static
uint64_t
global_auth_client_id
=
0
;
/* HELLOACL.REVOKE
* Synchronously revoke access from a user. */
int
RevokeCommand_RedisCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
global_auth_client_id
)
{
RedisModule_DisconnectClient
(
ctx
,
global_auth_client_id
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
else
{
return
RedisModule_ReplyWithError
(
ctx
,
"Global user currently not used"
);
}
}
/* HELLOACL.RESET
* Synchronously delete and re-create a module user. */
int
ResetCommand_RedisCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
RedisModule_FreeModuleUser
(
global
);
global
=
RedisModule_CreateModuleUser
(
"global"
);
RedisModule_SetModuleUserACL
(
global
,
"allcommands"
);
RedisModule_SetModuleUserACL
(
global
,
"allkeys"
);
RedisModule_SetModuleUserACL
(
global
,
"on"
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
/* Callback handler for user changes, use this to notify a module of
* changes to users authenticated by the module */
void
HelloACL_UserChanged
(
uint64_t
client_id
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
privdata
);
REDISMODULE_NOT_USED
(
client_id
);
global_auth_client_id
=
0
;
}
/* HELLOACL.AUTHGLOBAL
* Synchronously assigns a module user to the current context. */
int
AuthGlobalCommand_RedisCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
global_auth_client_id
)
{
return
RedisModule_ReplyWithError
(
ctx
,
"Global user currently used"
);
}
RedisModule_AuthenticateClientWithUser
(
ctx
,
global
,
HelloACL_UserChanged
,
NULL
,
&
global_auth_client_id
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
#define TIMEOUT_TIME 1000
/* Reply callback for auth command HELLOACL.AUTHASYNC */
int
HelloACL_Reply
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
size_t
length
;
RedisModuleString
*
user_string
=
RedisModule_GetBlockedClientPrivateData
(
ctx
);
const
char
*
name
=
RedisModule_StringPtrLen
(
user_string
,
&
length
);
if
(
RedisModule_AuthenticateClientWithACLUser
(
ctx
,
name
,
length
,
NULL
,
NULL
,
NULL
)
==
REDISMODULE_ERR
)
{
return
RedisModule_ReplyWithError
(
ctx
,
"Invalid Username or password"
);
}
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
/* Timeout callback for auth command HELLOACL.AUTHASYNC */
int
HelloACL_Timeout
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"Request timedout"
);
}
/* Private data frees data for HELLOACL.AUTHASYNC command. */
void
HelloACL_FreeData
(
RedisModuleCtx
*
ctx
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
ctx
);
RedisModule_FreeString
(
NULL
,
privdata
);
}
/* Background authentication can happen here. */
void
*
HelloACL_ThreadMain
(
void
*
args
)
{
void
**
targs
=
args
;
RedisModuleBlockedClient
*
bc
=
targs
[
0
];
RedisModuleString
*
user
=
targs
[
1
];
RedisModule_Free
(
targs
);
RedisModule_UnblockClient
(
bc
,
user
);
return
NULL
;
}
/* HELLOACL.AUTHASYNC
* Asynchronously assigns an ACL user to the current context. */
int
AuthAsyncCommand_RedisCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
2
)
return
RedisModule_WrongArity
(
ctx
);
pthread_t
tid
;
RedisModuleBlockedClient
*
bc
=
RedisModule_BlockClient
(
ctx
,
HelloACL_Reply
,
HelloACL_Timeout
,
HelloACL_FreeData
,
TIMEOUT_TIME
);
void
**
targs
=
RedisModule_Alloc
(
sizeof
(
void
*
)
*
2
);
targs
[
0
]
=
bc
;
targs
[
1
]
=
RedisModule_CreateStringFromString
(
NULL
,
argv
[
1
]);
if
(
pthread_create
(
&
tid
,
NULL
,
HelloACL_ThreadMain
,
targs
)
!=
0
)
{
RedisModule_AbortBlock
(
bc
);
return
RedisModule_ReplyWithError
(
ctx
,
"-ERR Can't start thread"
);
}
return
REDISMODULE_OK
;
}
/* This function must be present on each Redis module. It is used in order to
* register the commands into the Redis server. */
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"helloacl"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"helloacl.reset"
,
ResetCommand_RedisCommand
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"helloacl.revoke"
,
RevokeCommand_RedisCommand
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"helloacl.authglobal"
,
AuthGlobalCommand_RedisCommand
,
"no-auth"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"helloacl.authasync"
,
AuthAsyncCommand_RedisCommand
,
"no-auth"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
global
=
RedisModule_CreateModuleUser
(
"global"
);
RedisModule_SetModuleUserACL
(
global
,
"allcommands"
);
RedisModule_SetModuleUserACL
(
global
,
"allkeys"
);
RedisModule_SetModuleUserACL
(
global
,
"on"
);
global_auth_client_id
=
0
;
return
REDISMODULE_OK
;
}
src/networking.c
View file @
034dcf18
...
@@ -154,6 +154,9 @@ client *createClient(connection *conn) {
...
@@ -154,6 +154,9 @@ client *createClient(connection *conn) {
c
->
peerid
=
NULL
;
c
->
peerid
=
NULL
;
c
->
client_list_node
=
NULL
;
c
->
client_list_node
=
NULL
;
c
->
client_tracking_redirection
=
0
;
c
->
client_tracking_redirection
=
0
;
c
->
auth_callback
=
NULL
;
c
->
auth_callback_privdata
=
NULL
;
c
->
auth_module
=
NULL
;
listSetFreeMethod
(
c
->
pubsub_patterns
,
decrRefCountVoid
);
listSetFreeMethod
(
c
->
pubsub_patterns
,
decrRefCountVoid
);
listSetMatchMethod
(
c
->
pubsub_patterns
,
listMatchObjects
);
listSetMatchMethod
(
c
->
pubsub_patterns
,
listMatchObjects
);
if
(
conn
)
linkClient
(
c
);
if
(
conn
)
linkClient
(
c
);
...
@@ -1051,6 +1054,9 @@ void freeClient(client *c) {
...
@@ -1051,6 +1054,9 @@ void freeClient(client *c) {
c
);
c
);
}
}
/* Notify module system that this client auth status changed. */
moduleNotifyUserChanged
(
c
);
/* If it is our master that's beging disconnected we should make sure
/* If it is our master that's beging disconnected we should make sure
* to cache the state to try a partial resynchronization later.
* to cache the state to try a partial resynchronization later.
*
*
...
...
src/redismodule.h
View file @
034dcf18
...
@@ -395,6 +395,8 @@ typedef struct RedisModuleCommandFilter RedisModuleCommandFilter;
...
@@ -395,6 +395,8 @@ typedef struct RedisModuleCommandFilter RedisModuleCommandFilter;
typedef
struct
RedisModuleInfoCtx
RedisModuleInfoCtx
;
typedef
struct
RedisModuleInfoCtx
RedisModuleInfoCtx
;
typedef
struct
RedisModuleServerInfoData
RedisModuleServerInfoData
;
typedef
struct
RedisModuleServerInfoData
RedisModuleServerInfoData
;
typedef
struct
RedisModuleScanCursor
RedisModuleScanCursor
;
typedef
struct
RedisModuleScanCursor
RedisModuleScanCursor
;
typedef
struct
RedisModuleUser
RedisModuleUser
;
typedef
struct
RedisModuleAuthCtx
RedisModuleAuthCtx
;
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
...
@@ -414,6 +416,7 @@ typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *us
...
@@ -414,6 +416,7 @@ typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *us
typedef
void
(
*
RedisModuleInfoFunc
)(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
);
typedef
void
(
*
RedisModuleInfoFunc
)(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
);
typedef
void
(
*
RedisModuleScanCB
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
keyname
,
RedisModuleKey
*
key
,
void
*
privdata
);
typedef
void
(
*
RedisModuleScanCB
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
keyname
,
RedisModuleKey
*
key
,
void
*
privdata
);
typedef
void
(
*
RedisModuleScanKeyCB
)(
RedisModuleKey
*
key
,
RedisModuleString
*
field
,
RedisModuleString
*
value
,
void
*
privdata
);
typedef
void
(
*
RedisModuleScanKeyCB
)(
RedisModuleKey
*
key
,
RedisModuleString
*
field
,
RedisModuleString
*
value
,
void
*
privdata
);
typedef
void
(
*
RedisModuleUserChangedFunc
)
(
uint64_t
client_id
,
void
*
privdata
);
#define REDISMODULE_TYPE_METHOD_VERSION 2
#define REDISMODULE_TYPE_METHOD_VERSION 2
typedef
struct
RedisModuleTypeMethods
{
typedef
struct
RedisModuleTypeMethods
{
...
@@ -613,7 +616,7 @@ void REDISMODULE_API_FUNC(RedisModule_ScanCursorRestart)(RedisModuleScanCursor *
...
@@ -613,7 +616,7 @@ void REDISMODULE_API_FUNC(RedisModule_ScanCursorRestart)(RedisModuleScanCursor *
void
REDISMODULE_API_FUNC
(
RedisModule_ScanCursorDestroy
)(
RedisModuleScanCursor
*
cursor
);
void
REDISMODULE_API_FUNC
(
RedisModule_ScanCursorDestroy
)(
RedisModuleScanCursor
*
cursor
);
int
REDISMODULE_API_FUNC
(
RedisModule_Scan
)(
RedisModuleCtx
*
ctx
,
RedisModuleScanCursor
*
cursor
,
RedisModuleScanCB
fn
,
void
*
privdata
);
int
REDISMODULE_API_FUNC
(
RedisModule_Scan
)(
RedisModuleCtx
*
ctx
,
RedisModuleScanCursor
*
cursor
,
RedisModuleScanCB
fn
,
void
*
privdata
);
int
REDISMODULE_API_FUNC
(
RedisModule_ScanKey
)(
RedisModuleKey
*
key
,
RedisModuleScanCursor
*
cursor
,
RedisModuleScanKeyCB
fn
,
void
*
privdata
);
int
REDISMODULE_API_FUNC
(
RedisModule_ScanKey
)(
RedisModuleKey
*
key
,
RedisModuleScanCursor
*
cursor
,
RedisModuleScanKeyCB
fn
,
void
*
privdata
);
#define REDISMODULE_EXPERIMENTAL_API
/* Experimental APIs */
/* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API
#ifdef REDISMODULE_EXPERIMENTAL_API
#define REDISMODULE_EXPERIMENTAL_API_VERSION 3
#define REDISMODULE_EXPERIMENTAL_API_VERSION 3
...
@@ -660,6 +663,12 @@ int REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode);
...
@@ -660,6 +663,12 @@ int REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode);
int
REDISMODULE_API_FUNC
(
RedisModule_KillForkChild
)(
int
child_pid
);
int
REDISMODULE_API_FUNC
(
RedisModule_KillForkChild
)(
int
child_pid
);
float
REDISMODULE_API_FUNC
(
RedisModule_GetUsedMemoryRatio
)();
float
REDISMODULE_API_FUNC
(
RedisModule_GetUsedMemoryRatio
)();
size_t
REDISMODULE_API_FUNC
(
RedisModule_MallocSize
)(
void
*
ptr
);
size_t
REDISMODULE_API_FUNC
(
RedisModule_MallocSize
)(
void
*
ptr
);
RedisModuleUser
*
REDISMODULE_API_FUNC
(
RedisModule_CreateModuleUser
)(
const
char
*
name
);
void
REDISMODULE_API_FUNC
(
RedisModule_FreeModuleUser
)(
RedisModuleUser
*
user
);
int
REDISMODULE_API_FUNC
(
RedisModule_SetModuleUserACL
)(
RedisModuleUser
*
user
,
const
char
*
acl
);
int
REDISMODULE_API_FUNC
(
RedisModule_AuthenticateClientWithACLUser
)(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
size_t
len
,
RedisModuleUserChangedFunc
callback
,
void
*
privdata
,
uint64_t
*
client_id
);
int
REDISMODULE_API_FUNC
(
RedisModule_AuthenticateClientWithUser
)(
RedisModuleCtx
*
ctx
,
RedisModuleUser
*
user
,
RedisModuleUserChangedFunc
callback
,
void
*
privdata
,
uint64_t
*
client_id
);
void
REDISMODULE_API_FUNC
(
RedisModule_DeauthenticateAndCloseClient
)(
RedisModuleCtx
*
ctx
,
uint64_t
client_id
);
#endif
#endif
#define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX)
#define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX)
...
@@ -891,6 +900,12 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
...
@@ -891,6 +900,12 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
KillForkChild
);
REDISMODULE_GET_API
(
KillForkChild
);
REDISMODULE_GET_API
(
GetUsedMemoryRatio
);
REDISMODULE_GET_API
(
GetUsedMemoryRatio
);
REDISMODULE_GET_API
(
MallocSize
);
REDISMODULE_GET_API
(
MallocSize
);
REDISMODULE_GET_API
(
CreateModuleUser
);
REDISMODULE_GET_API
(
FreeModuleUser
);
REDISMODULE_GET_API
(
SetModuleUserACL
);
REDISMODULE_GET_API
(
DeauthenticateAndCloseClient
);
REDISMODULE_GET_API
(
AuthenticateClientWithACLUser
);
REDISMODULE_GET_API
(
AuthenticateClientWithUser
);
#endif
#endif
if
(
RedisModule_IsModuleNameBusy
&&
RedisModule_IsModuleNameBusy
(
name
))
return
REDISMODULE_ERR
;
if
(
RedisModule_IsModuleNameBusy
&&
RedisModule_IsModuleNameBusy
(
name
))
return
REDISMODULE_ERR
;
...
...
src/sentinel.c
View file @
034dcf18
...
@@ -461,8 +461,8 @@ struct redisCommand sentinelcmds[] = {
...
@@ -461,8 +461,8 @@ struct redisCommand sentinelcmds[] = {
{
"role"
,
sentinelRoleCommand
,
1
,
"ok-loading"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"role"
,
sentinelRoleCommand
,
1
,
"ok-loading"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"client"
,
clientCommand
,
-
2
,
"read-only no-script"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"client"
,
clientCommand
,
-
2
,
"read-only no-script"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"shutdown"
,
shutdownCommand
,
-
1
,
""
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"shutdown"
,
shutdownCommand
,
-
1
,
""
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"auth"
,
authCommand
,
2
,
"no-script ok-loading ok-stale fast"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"auth"
,
authCommand
,
2
,
"
no-auth
no-script ok-loading ok-stale fast"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
},
{
"hello"
,
helloCommand
,
-
2
,
"no-script fast"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
}
{
"hello"
,
helloCommand
,
-
2
,
"
no-auth
no-script fast"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
}
};
};
/* This function overwrites a few normal Redis config default with Sentinel
/* This function overwrites a few normal Redis config default with Sentinel
...
...
src/server.c
View file @
034dcf18
...
@@ -629,7 +629,7 @@ struct redisCommand redisCommandTable[] = {
...
@@ -629,7 +629,7 @@ struct redisCommand redisCommandTable[] = {
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
{
"auth"
,
authCommand
,
-
2
,
{
"auth"
,
authCommand
,
-
2
,
"no-script ok-loading ok-stale fast no-monitor no-slowlog @connection"
,
"
no-auth
no-script ok-loading ok-stale fast no-monitor no-slowlog @connection"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
/* We don't allow PING during loading since in Redis PING is used as
/* We don't allow PING during loading since in Redis PING is used as
...
@@ -824,7 +824,7 @@ struct redisCommand redisCommandTable[] = {
...
@@ -824,7 +824,7 @@ struct redisCommand redisCommandTable[] = {
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
{
"hello"
,
helloCommand
,
-
2
,
{
"hello"
,
helloCommand
,
-
2
,
"no-script fast no-monitor no-slowlog @connection"
,
"
no-auth
no-script fast no-monitor no-slowlog @connection"
,
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
0
,
NULL
,
0
,
0
,
0
,
0
,
0
,
0
},
/* EVAL can modify the dataset, however it is not flagged as a write
/* EVAL can modify the dataset, however it is not flagged as a write
...
@@ -2925,6 +2925,8 @@ int populateCommandTableParseFlags(struct redisCommand *c, char *strflags) {
...
@@ -2925,6 +2925,8 @@ int populateCommandTableParseFlags(struct redisCommand *c, char *strflags) {
c
->
flags
|=
CMD_ASKING
;
c
->
flags
|=
CMD_ASKING
;
}
else
if
(
!
strcasecmp
(
flag
,
"fast"
))
{
}
else
if
(
!
strcasecmp
(
flag
,
"fast"
))
{
c
->
flags
|=
CMD_FAST
|
CMD_CATEGORY_FAST
;
c
->
flags
|=
CMD_FAST
|
CMD_CATEGORY_FAST
;
}
else
if
(
!
strcasecmp
(
flag
,
"no-auth"
))
{
c
->
flags
|=
CMD_NO_AUTH
;
}
else
{
}
else
{
/* Parse ACL categories here if the flag name starts with @. */
/* Parse ACL categories here if the flag name starts with @. */
uint64_t
catflag
;
uint64_t
catflag
;
...
@@ -3345,8 +3347,9 @@ int processCommand(client *c) {
...
@@ -3345,8 +3347,9 @@ int processCommand(client *c) {
DefaultUser
->
flags
&
USER_FLAG_DISABLED
)
&&
DefaultUser
->
flags
&
USER_FLAG_DISABLED
)
&&
!
c
->
authenticated
;
!
c
->
authenticated
;
if
(
auth_required
)
{
if
(
auth_required
)
{
/* AUTH and HELLO are valid even in non authenticated state. */
/* AUTH and HELLO and no auth modules are valid even in
if
(
c
->
cmd
->
proc
!=
authCommand
&&
c
->
cmd
->
proc
!=
helloCommand
)
{
* non-authenticated state. */
if
(
!
(
c
->
cmd
->
flags
&
CMD_NO_AUTH
))
{
flagTransaction
(
c
);
flagTransaction
(
c
);
addReply
(
c
,
shared
.
noautherr
);
addReply
(
c
,
shared
.
noautherr
);
return
C_OK
;
return
C_OK
;
...
...
src/server.h
View file @
034dcf18
...
@@ -166,33 +166,34 @@ typedef long long ustime_t; /* microsecond time type. */
...
@@ -166,33 +166,34 @@ typedef long long ustime_t; /* microsecond time type. */
#define CMD_SKIP_SLOWLOG (1ULL<<12)
/* "no-slowlog" flag */
#define CMD_SKIP_SLOWLOG (1ULL<<12)
/* "no-slowlog" flag */
#define CMD_ASKING (1ULL<<13)
/* "cluster-asking" flag */
#define CMD_ASKING (1ULL<<13)
/* "cluster-asking" flag */
#define CMD_FAST (1ULL<<14)
/* "fast" flag */
#define CMD_FAST (1ULL<<14)
/* "fast" flag */
#define CMD_NO_AUTH (1ULL<<15)
/* "no-auth" flag */
/* Command flags used by the module system. */
/* Command flags used by the module system. */
#define CMD_MODULE_GETKEYS (1ULL<<1
5
)
/* Use the modules getkeys interface. */
#define CMD_MODULE_GETKEYS (1ULL<<1
6
)
/* Use the modules getkeys interface. */
#define CMD_MODULE_NO_CLUSTER (1ULL<<1
6
)
/* Deny on Redis Cluster. */
#define CMD_MODULE_NO_CLUSTER (1ULL<<1
7
)
/* Deny on Redis Cluster. */
/* Command flags that describe ACLs categories. */
/* Command flags that describe ACLs categories. */
#define CMD_CATEGORY_KEYSPACE (1ULL<<1
7
)
#define CMD_CATEGORY_KEYSPACE (1ULL<<1
8
)
#define CMD_CATEGORY_READ (1ULL<<1
8
)
#define CMD_CATEGORY_READ (1ULL<<1
9
)
#define CMD_CATEGORY_WRITE (1ULL<<
19
)
#define CMD_CATEGORY_WRITE (1ULL<<
20
)
#define CMD_CATEGORY_SET (1ULL<<2
0
)
#define CMD_CATEGORY_SET (1ULL<<2
1
)
#define CMD_CATEGORY_SORTEDSET (1ULL<<2
1
)
#define CMD_CATEGORY_SORTEDSET (1ULL<<2
2
)
#define CMD_CATEGORY_LIST (1ULL<<2
2
)
#define CMD_CATEGORY_LIST (1ULL<<2
3
)
#define CMD_CATEGORY_HASH (1ULL<<2
3
)
#define CMD_CATEGORY_HASH (1ULL<<2
4
)
#define CMD_CATEGORY_STRING (1ULL<<2
4
)
#define CMD_CATEGORY_STRING (1ULL<<2
5
)
#define CMD_CATEGORY_BITMAP (1ULL<<2
5
)
#define CMD_CATEGORY_BITMAP (1ULL<<2
6
)
#define CMD_CATEGORY_HYPERLOGLOG (1ULL<<2
6
)
#define CMD_CATEGORY_HYPERLOGLOG (1ULL<<2
7
)
#define CMD_CATEGORY_GEO (1ULL<<2
7
)
#define CMD_CATEGORY_GEO (1ULL<<2
8
)
#define CMD_CATEGORY_STREAM (1ULL<<2
8
)
#define CMD_CATEGORY_STREAM (1ULL<<2
9
)
#define CMD_CATEGORY_PUBSUB (1ULL<<
29
)
#define CMD_CATEGORY_PUBSUB (1ULL<<
30
)
#define CMD_CATEGORY_ADMIN (1ULL<<3
0
)
#define CMD_CATEGORY_ADMIN (1ULL<<3
1
)
#define CMD_CATEGORY_FAST (1ULL<<3
1
)
#define CMD_CATEGORY_FAST (1ULL<<3
2
)
#define CMD_CATEGORY_SLOW (1ULL<<3
2
)
#define CMD_CATEGORY_SLOW (1ULL<<3
3
)
#define CMD_CATEGORY_BLOCKING (1ULL<<3
3
)
#define CMD_CATEGORY_BLOCKING (1ULL<<3
4
)
#define CMD_CATEGORY_DANGEROUS (1ULL<<3
4
)
#define CMD_CATEGORY_DANGEROUS (1ULL<<3
5
)
#define CMD_CATEGORY_CONNECTION (1ULL<<3
5
)
#define CMD_CATEGORY_CONNECTION (1ULL<<3
6
)
#define CMD_CATEGORY_TRANSACTION (1ULL<<3
6
)
#define CMD_CATEGORY_TRANSACTION (1ULL<<3
7
)
#define CMD_CATEGORY_SCRIPTING (1ULL<<3
7
)
#define CMD_CATEGORY_SCRIPTING (1ULL<<3
8
)
/* AOF states */
/* AOF states */
#define AOF_OFF 0
/* AOF is off */
#define AOF_OFF 0
/* AOF is off */
...
@@ -480,6 +481,10 @@ typedef void (*moduleTypeDigestFunc)(struct RedisModuleDigest *digest, void *val
...
@@ -480,6 +481,10 @@ typedef void (*moduleTypeDigestFunc)(struct RedisModuleDigest *digest, void *val
typedef
size_t
(
*
moduleTypeMemUsageFunc
)(
const
void
*
value
);
typedef
size_t
(
*
moduleTypeMemUsageFunc
)(
const
void
*
value
);
typedef
void
(
*
moduleTypeFreeFunc
)(
void
*
value
);
typedef
void
(
*
moduleTypeFreeFunc
)(
void
*
value
);
/* TODO */
typedef
void
(
*
RedisModuleUserChangedFunc
)
(
uint64_t
client_id
,
void
*
privdata
);
/* The module type, which is referenced in each value of a given type, defines
/* The module type, which is referenced in each value of a given type, defines
* the methods and links to the module exporting the type. */
* the methods and links to the module exporting the type. */
typedef
struct
RedisModuleType
{
typedef
struct
RedisModuleType
{
...
@@ -798,6 +803,13 @@ typedef struct client {
...
@@ -798,6 +803,13 @@ typedef struct client {
list
*
pubsub_patterns
;
/* patterns a client is interested in (SUBSCRIBE) */
list
*
pubsub_patterns
;
/* patterns a client is interested in (SUBSCRIBE) */
sds
peerid
;
/* Cached peer ID. */
sds
peerid
;
/* Cached peer ID. */
listNode
*
client_list_node
;
/* list node in client list */
listNode
*
client_list_node
;
/* list node in client list */
RedisModuleUserChangedFunc
auth_callback
;
/* Callback to execute when the
* authentication changes */
void
*
auth_callback_privdata
;
/* Private data that is passed when the auth
* callback is executed */
void
*
auth_module
;
/* The module that owns the callback, which is used
* to disconnect the client if the module is
* unloaded to allow for cleanup. */
/* If this client is in tracking mode and this field is non zero,
/* If this client is in tracking mode and this field is non zero,
* invalidation messages for keys fetched by this client will be send to
* invalidation messages for keys fetched by this client will be send to
...
@@ -1516,6 +1528,7 @@ void processModuleLoadingProgressEvent(int is_aof);
...
@@ -1516,6 +1528,7 @@ void processModuleLoadingProgressEvent(int is_aof);
int
moduleTryServeClientBlockedOnKey
(
client
*
c
,
robj
*
key
);
int
moduleTryServeClientBlockedOnKey
(
client
*
c
,
robj
*
key
);
void
moduleUnblockClient
(
client
*
c
);
void
moduleUnblockClient
(
client
*
c
);
int
moduleClientIsBlockedOnKeys
(
client
*
c
);
int
moduleClientIsBlockedOnKeys
(
client
*
c
);
void
moduleNotifyUserChanged
(
client
*
c
);
/* Utils */
/* Utils */
long
long
ustime
(
void
);
long
long
ustime
(
void
);
...
@@ -1809,6 +1822,8 @@ int ACLLoadConfiguredUsers(void);
...
@@ -1809,6 +1822,8 @@ int ACLLoadConfiguredUsers(void);
sds
ACLDescribeUser
(
user
*
u
);
sds
ACLDescribeUser
(
user
*
u
);
void
ACLLoadUsersAtStartup
(
void
);
void
ACLLoadUsersAtStartup
(
void
);
void
addReplyCommandCategories
(
client
*
c
,
struct
redisCommand
*
cmd
);
void
addReplyCommandCategories
(
client
*
c
,
struct
redisCommand
*
cmd
);
user
*
ACLCreateUnlinkedUser
();
void
ACLFreeUserAndKillClients
(
user
*
u
);
/* Sorted sets data type */
/* Sorted sets data type */
...
...
tests/modules/Makefile
View file @
034dcf18
...
@@ -21,7 +21,8 @@ TEST_MODULES = \
...
@@ -21,7 +21,8 @@ TEST_MODULES = \
hooks.so
\
hooks.so
\
blockonkeys.so
\
blockonkeys.so
\
scan.so
\
scan.so
\
datatype.so
datatype.so
\
auth.so
.PHONY
:
all
.PHONY
:
all
...
...
tests/modules/auth.c
0 → 100644
View file @
034dcf18
/* ACL API example - An example of performing custom password authentication
*
* -----------------------------------------------------------------------------
*
* Copyright 2019 Amazon.com, Inc. or its affiliates.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
// A simple global user
static
RedisModuleUser
*
global
;
static
long
long
client_change_delta
=
0
;
void
UserChangedCallback
(
uint64_t
client_id
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
privdata
);
REDISMODULE_NOT_USED
(
client_id
);
client_change_delta
++
;
}
int
Auth_CreateModuleUser
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
global
)
{
RedisModule_FreeModuleUser
(
global
);
}
global
=
RedisModule_CreateModuleUser
(
"global"
);
RedisModule_SetModuleUserACL
(
global
,
"allcommands"
);
RedisModule_SetModuleUserACL
(
global
,
"allkeys"
);
RedisModule_SetModuleUserACL
(
global
,
"on"
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
int
Auth_AuthModuleUser
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
uint64_t
client_id
;
RedisModule_AuthenticateClientWithUser
(
ctx
,
global
,
UserChangedCallback
,
NULL
,
&
client_id
);
return
RedisModule_ReplyWithLongLong
(
ctx
,
(
uint64_t
)
client_id
);
}
int
Auth_AuthRealUser
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
2
)
return
RedisModule_WrongArity
(
ctx
);
size_t
length
;
uint64_t
client_id
;
RedisModuleString
*
user_string
=
argv
[
1
];
const
char
*
name
=
RedisModule_StringPtrLen
(
user_string
,
&
length
);
if
(
RedisModule_AuthenticateClientWithACLUser
(
ctx
,
name
,
length
,
UserChangedCallback
,
NULL
,
&
client_id
)
==
REDISMODULE_ERR
)
{
return
RedisModule_ReplyWithError
(
ctx
,
"Invalid user"
);
}
return
RedisModule_ReplyWithLongLong
(
ctx
,
(
uint64_t
)
client_id
);
}
int
Auth_ChangeCount
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
long
long
result
=
client_change_delta
;
client_change_delta
=
0
;
return
RedisModule_ReplyWithLongLong
(
ctx
,
result
);
}
/* This function must be present on each Redis module. It is used in order to
* register the commands into the Redis server. */
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"testacl"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"auth.authrealuser"
,
Auth_AuthRealUser
,
"no-auth"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"auth.createmoduleuser"
,
Auth_CreateModuleUser
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"auth.authmoduleuser"
,
Auth_AuthModuleUser
,
"no-auth"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"auth.changecount"
,
Auth_ChangeCount
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
client_change_delta
=
0
;
return
REDISMODULE_OK
;
}
tests/unit/moduleapi/auth.tcl
0 → 100644
View file @
034dcf18
set testmodule
[
file normalize tests/modules/auth.so
]
start_server
{
tags
{
"modules"
}}
{
r module load $testmodule
test
{
Modules can create a user that can be authenticated
}
{
# Make sure we start authenticated with default user
r auth default
""
assert_equal
[
r acl whoami
]
"default"
r auth.createmoduleuser
set id
[
r auth.authmoduleuser
]
assert_equal
[
r client id
]
$id
# Verify returned id is the same as our current id and
# we are authenticated with the specified user
assert_equal
[
r acl whoami
]
"global"
}
test
{
De-authenticating clients is tracked and kills clients
}
{
assert_equal
[
r auth.changecount
]
0
r auth.createmoduleuser
# Catch the I/O exception that was thrown when Redis
# disconnected with us.
catch
{
[
r ping
]
}
e
assert_match
{
*I/O*
}
$e
# Check that a user change was registered
assert_equal
[
r auth.changecount
]
1
}
test
{
Modules cant authenticate with ACLs users that dont exist
}
{
catch
{
[
r auth.authrealuser auth-module-test-fake
]
}
e
assert_match
{
*Invalid user*
}
$e
}
test
{
Modules can authenticate with ACL users
}
{
assert_equal
[
r acl whoami
]
"default"
# Create user to auth into
r acl setuser auth-module-test on allkeys allcommands
set id
[
r auth.authrealuser auth-module-test
]
# Verify returned id is the same as our current id and
# we are authenticated with the specified user
assert_equal
[
r client id
]
$id
assert_equal
[
r acl whoami
]
"auth-module-test"
}
test
{
Client callback is called on user switch
}
{
assert_equal
[
r auth.changecount
]
0
# Auth again and validate change count
r auth.authrealuser auth-module-test
assert_equal
[
r auth.changecount
]
1
# Re-auth with the default user
r auth default
""
assert_equal
[
r auth.changecount
]
1
assert_equal
[
r acl whoami
]
"default"
# Re-auth with the default user again, to
# verify the callback isn't fired again
r auth default
""
assert_equal
[
r auth.changecount
]
0
assert_equal
[
r acl whoami
]
"default"
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment