Commit 058bbedc authored by Madelyn Olson's avatar Madelyn Olson
Browse files

Add module APIs for custom authentication

parent a15a5d70
...@@ -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;
......
...@@ -349,6 +349,22 @@ list *RedisModule_EventListeners; /* Global list of all the active events. */ ...@@ -349,6 +349,22 @@ 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 */
typedef void (*RedisModuleUserChangedFunc) (void *privdata);
typedef struct RedisModuleUser {
user *user; /* Reference to the real redis user */
} RedisModuleUser;
typedef struct RedisModuleAuthCtx {
RedisModuleUserChangedFunc callback; /* Callback when user is changed or
/* deleted. */
void *privdata; /* Private data for the callback */
client *authenticated_client; /* A reference to client that was
/* authenticated */
} RedisModuleAuthCtx;
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Prototypes * Prototypes
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
...@@ -711,6 +727,7 @@ int commandFlagsFromString(char *s) { ...@@ -711,6 +727,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;
...@@ -772,6 +789,9 @@ int commandFlagsFromString(char *s) { ...@@ -772,6 +789,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;
...@@ -5108,6 +5128,113 @@ int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remain ...@@ -5108,6 +5128,113 @@ 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 clients user has changed, since a module
* may be tracking extra meta data about the client. This is also called
* when a client is deleted. */
void moduleNotifyUserChanged(client *c) {
RedisModuleAuthCtx *auth_ctx = (RedisModuleAuthCtx *) c->auth_ctx;
if (auth_ctx) {
auth_ctx->callback(auth_ctx->privdata);
zfree(auth_ctx);
}
}
static int authenticateClientWithUser(RedisModuleCtx *ctx, user *user, RedisModuleAuthCtx *auth_ctx) {
if (auth_ctx && auth_ctx->authenticated_client) {
/* Prevent basic misuse of Auth contexts */
return REDISMODULE_ERR;
}
if (user->flags & USER_FLAG_DISABLED) {
return REDISMODULE_ERR;
}
moduleNotifyUserChanged(ctx->client);
ctx->client->user = user;
ctx->client->authenticated = 1;
if (auth_ctx) {
ctx->client->auth_ctx = auth_ctx;
auth_ctx->authenticated_client = ctx->client;
}
return REDISMODULE_OK;
}
/* Creates a new user that is unlinked from the main ACL user dictionary. These
* users behave the same way as those in ACL.c except for a few minor
* differences. These users do not exist within a namespace, and handling
* duplicate users is the responsibility of the calling module. These users are
* also not attached to the redis users dictionary, so they are not returned
* via ACL LIST or GETUSER. This also means that users created here must be
* updated with the SetUserACL function instead of through ACL SETUSER. */
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. */
int RM_FreeModuleUser(RedisModuleUser *user) {
ACLFreeUserAndKillClients(user->user);
zfree(user);
return REDISMODULE_OK;
}
/* Sets the user permission of a user created through the redis module
* interface. The syntax is the sam as ACL SETUSER, so refer to the
* documentation in acl.c for more information. */
int RM_SetModuleUserACL(RedisModuleUser *user, const char* acl) {
return ACLSetUser(user->user, acl, -1);
}
/* Authenticate the current contexts user with the provided module user.
* Throws an error if the user is disabled or the AuthCtx is misued */
int RM_AuthenticateClientWithUser(RedisModuleCtx *ctx, RedisModuleUser *module_user, RedisModuleAuthCtx *auth_ctx) {
return authenticateClientWithUser(ctx, module_user->user, auth_ctx);
}
/* Authenticate the current contexts user with the provided redis acl user.
* Throws an error if the user is disabled, the user doesn't exit,
* or the AuthCtx is misused. */
int RM_AuthenticateClientWithACLUser(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleAuthCtx *auth_ctx) {
user *acl_user = ACLGetUserByName(name, len);
if (!acl_user) {
return REDISMODULE_ERR;
}
return authenticateClientWithUser(ctx, acl_user, auth_ctx);
}
/* Create a redis Auth Ctx which can be used to notify the module when
* the client authenticates with a different user or to revoke access
* to the authenticated client. */
RedisModuleAuthCtx *RM_CreateAuthCtx(RedisModuleUserChangedFunc callback, void *privdata) {
RedisModuleAuthCtx *auth_ctx = zmalloc(sizeof(RedisModuleAuthCtx));
auth_ctx->callback = callback;
auth_ctx->privdata = privdata;
return auth_ctx;
}
/* Revokes the authentication that was granted during the specified
* AuthCtx. The method is not thread safe. */
void RM_RevokeAuthentication(RedisModuleAuthCtx *ctx) {
/* Revoking authentication frees the client today. */
freeClientAsync(ctx->authenticated_client);
}
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Modules Dictionary API * Modules Dictionary API
* *
...@@ -6971,4 +7098,12 @@ void moduleRegisterCoreAPI(void) { ...@@ -6971,4 +7098,12 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(BlockClientOnKeys); REGISTER_API(BlockClientOnKeys);
REGISTER_API(SignalKeyAsReady); REGISTER_API(SignalKeyAsReady);
REGISTER_API(GetBlockedClientReadyKey); REGISTER_API(GetBlockedClientReadyKey);
REGISTER_API(CreateModuleUser);
REGISTER_API(SetModuleUserACL);
REGISTER_API(FreeModuleUser);
REGISTER_API(RevokeAuthentication);
REGISTER_API(CreateAuthCtx);
REGISTER_API(AuthenticateClientWithACLUser);
REGISTER_API(AuthenticateClientWithUser);
} }
...@@ -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
......
/* 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 RedisModuleAuthCtx *global_auth_ctx;
/* 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_ctx) {
RedisModule_RevokeAuthentication(global_auth_ctx);
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(void *privdata) {
REDISMODULE_NOT_USED(privdata);
global_auth_ctx = NULL;
}
/* 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_ctx) {
return RedisModule_ReplyWithError(ctx, "Global user currently used");
}
RedisModuleAuthCtx *auth_ctx = RedisModule_CreateAuthCtx(HelloACL_UserChanged,NULL);
RedisModule_AuthenticateClientWithUser(ctx, global, auth_ctx);
global_auth_ctx = auth_ctx;
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) ==
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_ctx = NULL;
return REDISMODULE_OK;
}
...@@ -154,6 +154,7 @@ client *createClient(connection *conn) { ...@@ -154,6 +154,7 @@ 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_ctx = 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 +1052,9 @@ void freeClient(client *c) { ...@@ -1051,6 +1052,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.
* *
......
...@@ -392,6 +392,8 @@ typedef struct RedisModuleDictIter RedisModuleDictIter; ...@@ -392,6 +392,8 @@ typedef struct RedisModuleDictIter RedisModuleDictIter;
typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx; typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;
typedef struct RedisModuleCommandFilter RedisModuleCommandFilter; typedef struct RedisModuleCommandFilter RedisModuleCommandFilter;
typedef struct RedisModuleInfoCtx RedisModuleInfoCtx; typedef struct RedisModuleInfoCtx RedisModuleInfoCtx;
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);
...@@ -409,6 +411,7 @@ typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); ...@@ -409,6 +411,7 @@ typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter); typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);
typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data); typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report); typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);
typedef void (*RedisModuleUserChangedFunc)(void *privdata);
#define REDISMODULE_TYPE_METHOD_VERSION 2 #define REDISMODULE_TYPE_METHOD_VERSION 2
typedef struct RedisModuleTypeMethods { typedef struct RedisModuleTypeMethods {
...@@ -633,6 +636,13 @@ int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandF ...@@ -633,6 +636,13 @@ int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandF
int REDISMODULE_API_FUNC(RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data); int REDISMODULE_API_FUNC(RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data);
int REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode); 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);
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, RedisModuleAuthCtx *auth_ctx);
int REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *module_user, RedisModuleAuthCtx *auth_ctx);
void REDISMODULE_API_FUNC(RedisModule_RevokeAuthentication)(RedisModuleAuthCtx *ctx);
RedisModuleAuthCtx *REDISMODULE_API_FUNC(RedisModule_CreateAuthCtx)(RedisModuleUserChangedFunc callback, void *privdata);
#endif #endif
#define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX) #define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX)
...@@ -842,6 +852,14 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int ...@@ -842,6 +852,14 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(Fork); REDISMODULE_GET_API(Fork);
REDISMODULE_GET_API(ExitFromChild); REDISMODULE_GET_API(ExitFromChild);
REDISMODULE_GET_API(KillForkChild); REDISMODULE_GET_API(KillForkChild);
REDISMODULE_GET_API(CreateModuleUser);
REDISMODULE_GET_API(FreeModuleUser);
REDISMODULE_GET_API(SetModuleUserACL);
REDISMODULE_GET_API(RevokeAuthentication);
REDISMODULE_GET_API(CreateAuthCtx);
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;
......
...@@ -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
......
...@@ -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
...@@ -3008,6 +3008,8 @@ int populateCommandTableParseFlags(struct redisCommand *c, char *strflags) { ...@@ -3008,6 +3008,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;
...@@ -3423,8 +3425,9 @@ int processCommand(client *c) { ...@@ -3423,8 +3425,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;
......
...@@ -237,33 +237,34 @@ typedef long long mstime_t; /* millisecond time type. */ ...@@ -237,33 +237,34 @@ typedef long long mstime_t; /* millisecond 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<<15) /* Use the modules getkeys interface. */ #define CMD_MODULE_GETKEYS (1ULL<<16) /* Use the modules getkeys interface. */
#define CMD_MODULE_NO_CLUSTER (1ULL<<16) /* Deny on Redis Cluster. */ #define CMD_MODULE_NO_CLUSTER (1ULL<<17) /* Deny on Redis Cluster. */
/* Command flags that describe ACLs categories. */ /* Command flags that describe ACLs categories. */
#define CMD_CATEGORY_KEYSPACE (1ULL<<17) #define CMD_CATEGORY_KEYSPACE (1ULL<<18)
#define CMD_CATEGORY_READ (1ULL<<18) #define CMD_CATEGORY_READ (1ULL<<19)
#define CMD_CATEGORY_WRITE (1ULL<<19) #define CMD_CATEGORY_WRITE (1ULL<<20)
#define CMD_CATEGORY_SET (1ULL<<20) #define CMD_CATEGORY_SET (1ULL<<21)
#define CMD_CATEGORY_SORTEDSET (1ULL<<21) #define CMD_CATEGORY_SORTEDSET (1ULL<<22)
#define CMD_CATEGORY_LIST (1ULL<<22) #define CMD_CATEGORY_LIST (1ULL<<23)
#define CMD_CATEGORY_HASH (1ULL<<23) #define CMD_CATEGORY_HASH (1ULL<<24)
#define CMD_CATEGORY_STRING (1ULL<<24) #define CMD_CATEGORY_STRING (1ULL<<25)
#define CMD_CATEGORY_BITMAP (1ULL<<25) #define CMD_CATEGORY_BITMAP (1ULL<<26)
#define CMD_CATEGORY_HYPERLOGLOG (1ULL<<26) #define CMD_CATEGORY_HYPERLOGLOG (1ULL<<27)
#define CMD_CATEGORY_GEO (1ULL<<27) #define CMD_CATEGORY_GEO (1ULL<<28)
#define CMD_CATEGORY_STREAM (1ULL<<28) #define CMD_CATEGORY_STREAM (1ULL<<29)
#define CMD_CATEGORY_PUBSUB (1ULL<<29) #define CMD_CATEGORY_PUBSUB (1ULL<<30)
#define CMD_CATEGORY_ADMIN (1ULL<<30) #define CMD_CATEGORY_ADMIN (1ULL<<31)
#define CMD_CATEGORY_FAST (1ULL<<31) #define CMD_CATEGORY_FAST (1ULL<<32)
#define CMD_CATEGORY_SLOW (1ULL<<32) #define CMD_CATEGORY_SLOW (1ULL<<33)
#define CMD_CATEGORY_BLOCKING (1ULL<<33) #define CMD_CATEGORY_BLOCKING (1ULL<<34)
#define CMD_CATEGORY_DANGEROUS (1ULL<<34) #define CMD_CATEGORY_DANGEROUS (1ULL<<35)
#define CMD_CATEGORY_CONNECTION (1ULL<<35) #define CMD_CATEGORY_CONNECTION (1ULL<<36)
#define CMD_CATEGORY_TRANSACTION (1ULL<<36) #define CMD_CATEGORY_TRANSACTION (1ULL<<37)
#define CMD_CATEGORY_SCRIPTING (1ULL<<37) #define CMD_CATEGORY_SCRIPTING (1ULL<<38)
/* AOF states */ /* AOF states */
#define AOF_OFF 0 /* AOF is off */ #define AOF_OFF 0 /* AOF is off */
...@@ -892,6 +893,7 @@ typedef struct client { ...@@ -892,6 +893,7 @@ 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 */
void *auth_ctx; /* Structured used to track module authentication */
/* 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
...@@ -1606,6 +1608,7 @@ void processModuleLoadingProgressEvent(int is_aof); ...@@ -1606,6 +1608,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);
...@@ -1899,6 +1902,8 @@ int ACLLoadConfiguredUsers(void); ...@@ -1899,6 +1902,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 */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment