2018-12-21 17:16:22 +01:00
/*
* Copyright ( c ) 2018 , Salvatore Sanfilippo < antirez at gmail dot com >
* 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 .
*/
# include "server.h"
2019-09-12 12:33:22 +02:00
# include "sha256.h"
2019-02-21 16:50:28 +01:00
# include <fcntl.h>
2020-04-15 16:12:06 +02:00
# include <ctype.h>
2018-12-21 17:16:22 +01:00
2019-01-10 16:39:32 +01:00
/* =============================================================================
* Global state for ACLs
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
rax * Users ; /* Table mapping usernames to user structures. */
2019-02-01 12:20:09 +01:00
user * DefaultUser ; /* Global reference to the default user.
Every new connection is associated to it , if no
AUTH or HELLO is used to authenticate with a
different user . */
list * UsersToLoad ; /* This is a list of users found in the configuration file
2024-05-08 15:17:32 -04:00
that we ' ll need to load in the final stage of the server
2019-02-01 12:20:09 +01:00
initialization , after all the modules are already
loaded . Every list element is a NULL terminated
array of SDS pointers : the first is the user name ,
all the remaining pointers are ACL rules in the same
format as ACLSetUser ( ) . */
2020-01-27 18:37:52 +01:00
list * ACLLog ; /* Our security log, the user is able to inspect that
using the ACL LOG command . */
2019-01-10 16:39:32 +01:00
2023-02-02 12:12:16 -08:00
long long ACLLogEntryCount = 0 ; /* Number of ACL log entries created */
2020-10-19 00:33:55 -04:00
static rax * commandId = NULL ; /* Command name to id mapping */
static unsigned long nextid = 0 ; /* Next command id that has not been assigned */
2023-08-30 13:01:24 -07:00
# define ACL_MAX_CATEGORIES 64 /* Maximum number of command categories */
2019-01-23 16:47:29 +01:00
struct ACLCategoryItem {
2023-08-30 13:01:24 -07:00
char * name ;
2019-01-23 16:47:29 +01:00
uint64_t flag ;
2024-04-03 10:47:26 -07:00
} ACLDefaultCommandCategories [ ] = { /* See valkey.conf for details on each category. */
Auto-generate the command table from JSON files (#9656)
Delete the hardcoded command table and replace it with an auto-generated table, based
on a JSON file that describes the commands (each command must have a JSON file).
These JSON files are the SSOT of everything there is to know about Redis commands,
and it is reflected fully in COMMAND INFO.
These JSON files are used to generate commands.c (using a python script), which is then
committed to the repo and compiled.
The purpose is:
* Clients and proxies will be able to get much more info from redis, instead of relying on hard coded logic.
* drop the dependency between Redis-user and the commands.json in redis-doc.
* delete help.h and have redis-cli learn everything it needs to know just by issuing COMMAND (will be
done in a separate PR)
* redis.io should stop using commands.json and learn everything from Redis (ultimately one of the release
artifacts should be a large JSON, containing all the information about all of the commands, which will be
generated from COMMAND's reply)
* the byproduct of this is:
* module commands will be able to provide that info and possibly be more of a first-class citizens
* in theory, one may be able to generate a redis client library for a strictly typed language, by using this info.
### Interface changes
#### COMMAND INFO's reply change (and arg-less COMMAND)
Before this commit the reply at index 7 contained the key-specs list
and reply at index 8 contained the sub-commands list (Both unreleased).
Now, reply at index 7 is a map of:
- summary - short command description
- since - debut version
- group - command group
- complexity - complexity string
- doc-flags - flags used for documentation (e.g. "deprecated")
- deprecated-since - if deprecated, from which version?
- replaced-by - if deprecated, which command replaced it?
- history - a list of (version, what-changed) tuples
- hints - a list of strings, meant to provide hints for clients/proxies. see https://github.com/redis/redis/issues/9876
- arguments - an array of arguments. each element is a map, with the possibility of nesting (sub-arguments)
- key-specs - an array of keys specs (already in unstable, just changed location)
- subcommands - a list of sub-commands (already in unstable, just changed location)
- reply-schema - will be added in the future (see https://github.com/redis/redis/issues/9845)
more details on these can be found in https://github.com/redis/redis-doc/pull/1697
only the first three fields are mandatory
#### API changes (unreleased API obviously)
now they take RedisModuleCommand opaque pointer instead of looking up the command by name
- RM_CreateSubcommand
- RM_AddCommandKeySpec
- RM_SetCommandKeySpecBeginSearchIndex
- RM_SetCommandKeySpecBeginSearchKeyword
- RM_SetCommandKeySpecFindKeysRange
- RM_SetCommandKeySpecFindKeysKeynum
Currently, we did not add module API to provide additional information about their commands because
we couldn't agree on how the API should look like, see https://github.com/redis/redis/issues/9944.
### Somehow related changes
1. Literals should be in uppercase while placeholder in lowercase. Now all the GEO* command
will be documented with M|KM|FT|MI and can take both lowercase and uppercase
### Unrelated changes
1. Bugfix: no_madaory_keys was absent in COMMAND's reply
2. expose CMD_MODULE as "module" via COMMAND
3. have a dedicated uint64 for ACL categories (instead of having them in the same uint64 as command flags)
Co-authored-by: Itamar Haber <itamar@garantiadata.com>
2021-12-15 20:23:15 +01:00
{ " keyspace " , ACL_CATEGORY_KEYSPACE } ,
{ " read " , ACL_CATEGORY_READ } ,
{ " write " , ACL_CATEGORY_WRITE } ,
{ " set " , ACL_CATEGORY_SET } ,
{ " sortedset " , ACL_CATEGORY_SORTEDSET } ,
{ " list " , ACL_CATEGORY_LIST } ,
{ " hash " , ACL_CATEGORY_HASH } ,
{ " string " , ACL_CATEGORY_STRING } ,
{ " bitmap " , ACL_CATEGORY_BITMAP } ,
{ " hyperloglog " , ACL_CATEGORY_HYPERLOGLOG } ,
{ " geo " , ACL_CATEGORY_GEO } ,
{ " stream " , ACL_CATEGORY_STREAM } ,
{ " pubsub " , ACL_CATEGORY_PUBSUB } ,
{ " admin " , ACL_CATEGORY_ADMIN } ,
{ " fast " , ACL_CATEGORY_FAST } ,
{ " slow " , ACL_CATEGORY_SLOW } ,
{ " blocking " , ACL_CATEGORY_BLOCKING } ,
{ " dangerous " , ACL_CATEGORY_DANGEROUS } ,
{ " connection " , ACL_CATEGORY_CONNECTION } ,
{ " transaction " , ACL_CATEGORY_TRANSACTION } ,
{ " scripting " , ACL_CATEGORY_SCRIPTING } ,
2019-01-31 16:49:22 +01:00
{ NULL , 0 } /* Terminator. */
} ;
2023-08-30 13:01:24 -07:00
static struct ACLCategoryItem * ACLCommandCategories = NULL ;
static size_t nextCommandCategory = 0 ; /* Index of the next command category to be added */
/* Implements the ability to add to the list of ACL categories at runtime. Since each ACL category
* also requires a bit in the acl_categories flag , there is a limit to the number that can be added .
* The new ACL categories occupy the remaining bits of acl_categories flag , other than the bits
* occupied by the default ACL command categories .
*
* The optional ` flag ` argument allows the assignment of the ` acl_categories ` flag bit to the ACL category .
* When adding a new category , except for the default ACL command categories , this arguments should be ` 0 `
* to allow the function to assign the next available ` acl_categories ` flag bit to the new ACL category .
*
* returns 1 - > Added , 0 - > Failed ( out of space )
*
* This function is present here to gain access to the ACLCommandCategories array and add a new ACL category .
*/
int ACLAddCommandCategory ( const char * name , uint64_t flag ) {
if ( nextCommandCategory > = ACL_MAX_CATEGORIES ) return 0 ;
ACLCommandCategories [ nextCommandCategory ] . name = zstrdup ( name ) ;
ACLCommandCategories [ nextCommandCategory ] . flag = flag ! = 0 ? flag : ( 1ULL < < nextCommandCategory ) ;
nextCommandCategory + + ;
return 1 ;
}
/* Initializes ACLCommandCategories with default ACL categories and allocates space for
* new ACL categories .
*/
void ACLInitCommandCategories ( void ) {
ACLCommandCategories = zcalloc ( sizeof ( struct ACLCategoryItem ) * ( ACL_MAX_CATEGORIES + 1 ) ) ;
for ( int j = 0 ; ACLDefaultCommandCategories [ j ] . flag ; j + + ) {
serverAssert ( ACLAddCommandCategory ( ACLDefaultCommandCategories [ j ] . name , ACLDefaultCommandCategories [ j ] . flag ) ) ;
}
}
/* This function removes the specified number of categories from the trailing end of
* the ` ACLCommandCategories ` array .
* The purpose of this is to remove the categories added by modules that fail
* during the onload function .
*/
void ACLCleanupCategoriesOnFailure ( size_t num_acl_categories_added ) {
for ( size_t j = nextCommandCategory - num_acl_categories_added ; j < nextCommandCategory ; j + + ) {
zfree ( ACLCommandCategories [ j ] . name ) ;
ACLCommandCategories [ j ] . name = NULL ;
ACLCommandCategories [ j ] . flag = 0 ;
}
nextCommandCategory - = num_acl_categories_added ;
}
2019-01-31 16:49:22 +01:00
struct ACLUserFlag {
const char * name ;
uint64_t flag ;
} ACLUserFlags [ ] = {
Sanitize dump payload: ziplist, listpack, zipmap, intset, stream
When loading an encoded payload we will at least do a shallow validation to
check that the size that's encoded in the payload matches the size of the
allocation.
This let's us later use this encoded size to make sure the various offsets
inside encoded payload don't reach outside the allocation, if they do, we'll
assert/panic, but at least we won't segfault or smear memory.
We can also do 'deep' validation which runs on all the records of the encoded
payload and validates that they don't contain invalid offsets. This lets us
detect corruptions early and reject a RESTORE command rather than accepting
it and asserting (crashing) later when accessing that payload via some command.
configuration:
- adding ACL flag skip-sanitize-payload
- adding config sanitize-dump-payload [yes/no/clients]
For now, we don't have a good way to ensure MIGRATE in cluster resharding isn't
being slowed down by these sanitation, so i'm setting the default value to `no`,
but later on it should be set to `clients` by default.
changes:
- changing rdbReportError not to `exit` in RESTORE command
- adding a new stat to be able to later check if cluster MIGRATE isn't being
slowed down by sanitation.
2020-08-13 16:41:05 +03:00
/* Note: the order here dictates the emitted order at ACLDescribeUser */
2019-01-31 16:49:22 +01:00
{ " on " , USER_FLAG_ENABLED } ,
{ " off " , USER_FLAG_DISABLED } ,
{ " nopass " , USER_FLAG_NOPASS } ,
Sanitize dump payload: ziplist, listpack, zipmap, intset, stream
When loading an encoded payload we will at least do a shallow validation to
check that the size that's encoded in the payload matches the size of the
allocation.
This let's us later use this encoded size to make sure the various offsets
inside encoded payload don't reach outside the allocation, if they do, we'll
assert/panic, but at least we won't segfault or smear memory.
We can also do 'deep' validation which runs on all the records of the encoded
payload and validates that they don't contain invalid offsets. This lets us
detect corruptions early and reject a RESTORE command rather than accepting
it and asserting (crashing) later when accessing that payload via some command.
configuration:
- adding ACL flag skip-sanitize-payload
- adding config sanitize-dump-payload [yes/no/clients]
For now, we don't have a good way to ensure MIGRATE in cluster resharding isn't
being slowed down by these sanitation, so i'm setting the default value to `no`,
but later on it should be set to `clients` by default.
changes:
- changing rdbReportError not to `exit` in RESTORE command
- adding a new stat to be able to later check if cluster MIGRATE isn't being
slowed down by sanitation.
2020-08-13 16:41:05 +03:00
{ " skip-sanitize-payload " , USER_FLAG_SANITIZE_PAYLOAD_SKIP } ,
{ " sanitize-payload " , USER_FLAG_SANITIZE_PAYLOAD } ,
2019-01-31 16:49:22 +01:00
{ NULL , 0 } /* Terminator. */
2019-01-23 16:47:29 +01:00
} ;
2022-01-20 13:05:27 -08:00
struct ACLSelectorFlags {
const char * name ;
uint64_t flag ;
} ACLSelectorFlags [ ] = {
/* Note: the order here dictates the emitted order at ACLDescribeUser */
{ " allkeys " , SELECTOR_FLAG_ALLKEYS } ,
{ " allchannels " , SELECTOR_FLAG_ALLCHANNELS } ,
{ " allcommands " , SELECTOR_FLAG_ALLCOMMANDS } ,
{ NULL , 0 } /* Terminator. */
} ;
/* ACL selectors are private and not exposed outside of acl.c. */
typedef struct {
uint32_t flags ; /* See SELECTOR_FLAG_* */
/* The bit in allowed_commands is set if this user has the right to
* execute this command .
*
* If the bit for a given command is NOT set and the command has
2024-04-09 01:24:03 -07:00
* allowed first - args , the server will also check allowed_firstargs in order to
2022-01-20 13:05:27 -08:00
* understand if the command can be executed . */
uint64_t allowed_commands [ USER_COMMAND_BITS_COUNT / 64 ] ;
/* allowed_firstargs is used by ACL rules to block access to a command unless a
2022-03-08 19:35:37 +08:00
* specific argv [ 1 ] is given .
2022-01-20 13:05:27 -08:00
*
* For each command ID ( corresponding to the command bit set in allowed_commands ) ,
* This array points to an array of SDS strings , terminated by a NULL pointer ,
* with all the first - args that are allowed for this command . When no first - arg
* matching is used , the field is just set to NULL to avoid allocating
* USER_COMMAND_BITS_COUNT pointers . */
sds * * allowed_firstargs ;
list * patterns ; /* A list of allowed key patterns. If this field is NULL
the user cannot mention any key in a command , unless
the flag ALLKEYS is set in the user . */
list * channels ; /* A list of allowed Pub/Sub channel patterns. If this
field is NULL the user cannot mention any channel in a
` PUBLISH ` or [ P ] [ UNSUBSCRIBE ] command , unless the flag
ALLCHANNELS is set in the user . */
2022-11-03 10:14:56 -07:00
sds command_rules ; /* A string representation of the ordered categories and commands, this
* is used to regenerate the original ACL string for display . */
2022-01-20 13:05:27 -08:00
} aclSelector ;
void ACLResetFirstArgsForCommand ( aclSelector * selector , unsigned long id ) ;
void ACLResetFirstArgs ( aclSelector * selector ) ;
void ACLAddAllowedFirstArg ( aclSelector * selector , unsigned long id , const char * sub ) ;
2020-01-29 18:51:04 +01:00
void ACLFreeLogEntry ( void * le ) ;
2022-01-20 13:05:27 -08:00
int ACLSetSelector ( aclSelector * selector , const char * op , size_t oplen ) ;
2019-01-30 08:09:05 +01:00
2019-09-17 03:32:35 -07:00
/* The length of the string representation of a hashed password. */
2022-06-03 09:30:28 -07:00
# define HASH_PASSWORD_LEN (SHA256_BLOCK_SIZE*2)
2019-09-17 03:32:35 -07:00
2019-01-10 16:35:55 +01:00
/* =============================================================================
* Helper functions for the rest of the ACL implementation
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2018-12-21 17:16:22 +01:00
/* Return zero if strings are the same, non-zero if they are not.
* The comparison is performed in a way that prevents an attacker to obtain
* information about the nature of the strings just monitoring the execution
2022-06-03 09:30:28 -07:00
* time of the function . Note : The two strings must be the same length .
2018-12-21 17:16:22 +01:00
*/
2022-06-03 09:30:28 -07:00
int time_independent_strcmp ( char * a , char * b , int len ) {
2018-12-21 17:16:22 +01:00
int diff = 0 ;
2022-06-03 09:30:28 -07:00
for ( int j = 0 ; j < len ; j + + ) {
diff | = ( a [ j ] ^ b [ j ] ) ;
2018-12-21 17:16:22 +01:00
}
return diff ; /* If zero strings are the same. */
}
2019-09-12 12:33:22 +02:00
/* Given an SDS string, returns the SHA256 hex representation as a
* new SDS string . */
2019-09-12 12:54:57 +02:00
sds ACLHashPassword ( unsigned char * cleartext , size_t len ) {
2019-09-12 12:33:22 +02:00
SHA256_CTX ctx ;
unsigned char hash [ SHA256_BLOCK_SIZE ] ;
2019-09-17 03:32:35 -07:00
char hex [ HASH_PASSWORD_LEN ] ;
2019-09-12 12:33:22 +02:00
char * cset = " 0123456789abcdef " ;
sha256_init ( & ctx ) ;
2019-09-12 12:54:57 +02:00
sha256_update ( & ctx , ( unsigned char * ) cleartext , len ) ;
2019-09-12 12:33:22 +02:00
sha256_final ( & ctx , hash ) ;
for ( int j = 0 ; j < SHA256_BLOCK_SIZE ; j + + ) {
hex [ j * 2 ] = cset [ ( ( hash [ j ] & 0xF0 ) > > 4 ) ] ;
hex [ j * 2 + 1 ] = cset [ ( hash [ j ] & 0xF ) ] ;
}
2019-09-17 03:32:35 -07:00
return sdsnewlen ( hex , HASH_PASSWORD_LEN ) ;
2019-09-12 12:33:22 +02:00
}
2021-01-04 17:02:57 +02:00
/* Given a hash and the hash length, returns C_OK if it is a valid password
2020-05-14 11:07:51 -07:00
* hash , or C_ERR otherwise . */
int ACLCheckPasswordHash ( unsigned char * hash , int hashlen ) {
if ( hashlen ! = HASH_PASSWORD_LEN ) {
2021-01-04 17:02:57 +02:00
return C_ERR ;
2020-05-14 11:07:51 -07:00
}
2021-01-04 17:02:57 +02:00
2020-05-14 11:07:51 -07:00
/* Password hashes can only be characters that represent
2021-01-04 17:02:57 +02:00
* hexadecimal values , which are numbers and lowercase
2020-05-14 11:07:51 -07:00
* characters ' a ' through ' f ' . */
for ( int i = 0 ; i < HASH_PASSWORD_LEN ; i + + ) {
char c = hash [ i ] ;
if ( ( c < ' a ' | | c > ' f ' ) & & ( c < ' 0 ' | | c > ' 9 ' ) ) {
return C_ERR ;
}
}
return C_OK ;
}
2019-01-10 16:35:55 +01:00
/* =============================================================================
* Low level ACL API
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2020-04-15 16:39:42 +02:00
/* Return 1 if the specified string contains spaces or null characters.
* We do this for usernames and key patterns for simpler rewriting of
* ACL rules , presentation on ACL list , and to avoid subtle security bugs
* that may arise from parsing the rules in presence of escapes .
* The function returns 0 if the string has no spaces . */
int ACLStringHasSpaces ( const char * s , size_t len ) {
for ( size_t i = 0 ; i < len ; i + + ) {
if ( isspace ( s [ i ] ) | | s [ i ] = = 0 ) return 1 ;
}
return 0 ;
}
2019-01-23 16:59:09 +01:00
/* Given the category name the command returns the corresponding flag, or
* zero if there is no match . */
uint64_t ACLGetCommandCategoryFlagByName ( const char * name ) {
for ( int j = 0 ; ACLCommandCategories [ j ] . flag ! = 0 ; j + + ) {
if ( ! strcasecmp ( name , ACLCommandCategories [ j ] . name ) ) {
return ACLCommandCategories [ j ] . flag ;
}
}
return 0 ; /* No match. */
}
2021-09-09 07:40:33 -07:00
/* Method for searching for a user within a list of user definitions. The
* list contains an array of user arguments , and we are only
* searching the first argument , the username , for a match . */
int ACLListMatchLoadedUser ( void * definition , void * user ) {
sds * user_definition = definition ;
return sdscmp ( user_definition [ 0 ] , user ) = = 0 ;
}
2019-01-15 12:58:54 +01:00
/* Method for passwords/pattern comparison used for the user->passwords list
* so that we can search for items with listSearchKey ( ) . */
int ACLListMatchSds ( void * a , void * b ) {
return sdscmp ( a , b ) = = 0 ;
}
2020-01-07 21:09:44 -05:00
/* Method to free list elements from ACL users password/patterns lists. */
2019-01-28 12:11:11 +01:00
void ACLListFreeSds ( void * item ) {
sdsfree ( item ) ;
}
2020-01-07 21:09:44 -05:00
/* Method to duplicate list elements from ACL users password/patterns lists. */
2019-02-07 12:57:21 +01:00
void * ACLListDupSds ( void * item ) {
return sdsdup ( item ) ;
}
2022-01-20 13:05:27 -08:00
/* Structure used for handling key patterns with different key
* based permissions . */
typedef struct {
2023-09-08 21:10:17 +08:00
int flags ; /* The ACL key permission types for this key pattern */
2022-01-20 13:05:27 -08:00
sds pattern ; /* The pattern to match keys against */
} keyPattern ;
/* Create a new key pattern. */
keyPattern * ACLKeyPatternCreate ( sds pattern , int flags ) {
keyPattern * new = ( keyPattern * ) zmalloc ( sizeof ( keyPattern ) ) ;
new - > pattern = pattern ;
new - > flags = flags ;
return new ;
}
/* Free a key pattern and internal structures. */
void ACLKeyPatternFree ( keyPattern * pattern ) {
sdsfree ( pattern - > pattern ) ;
zfree ( pattern ) ;
}
/* Method for passwords/pattern comparison used for the user->passwords list
* so that we can search for items with listSearchKey ( ) . */
int ACLListMatchKeyPattern ( void * a , void * b ) {
return sdscmp ( ( ( keyPattern * ) a ) - > pattern , ( ( keyPattern * ) b ) - > pattern ) = = 0 ;
}
/* Method to free list elements from ACL users password/patterns lists. */
void ACLListFreeKeyPattern ( void * item ) {
ACLKeyPatternFree ( item ) ;
}
/* Method to duplicate list elements from ACL users password/patterns lists. */
void * ACLListDupKeyPattern ( void * item ) {
keyPattern * old = ( keyPattern * ) item ;
return ACLKeyPatternCreate ( sdsdup ( old - > pattern ) , old - > flags ) ;
}
/* Append the string representation of a key pattern onto the
* provided base string . */
sds sdsCatPatternString ( sds base , keyPattern * pat ) {
if ( pat - > flags = = ACL_ALL_PERMISSION ) {
base = sdscatlen ( base , " ~ " , 1 ) ;
} else if ( pat - > flags = = ACL_READ_PERMISSION ) {
base = sdscatlen ( base , " %R~ " , 3 ) ;
} else if ( pat - > flags = = ACL_WRITE_PERMISSION ) {
base = sdscatlen ( base , " %W~ " , 3 ) ;
} else {
serverPanic ( " Invalid key pattern flag detected " ) ;
}
return sdscatsds ( base , pat - > pattern ) ;
}
/* Create an empty selector with the provided set of initial
* flags . The selector will be default have no permissions . */
aclSelector * ACLCreateSelector ( int flags ) {
aclSelector * selector = zmalloc ( sizeof ( aclSelector ) ) ;
selector - > flags = flags | server . acl_pubsub_default ;
selector - > patterns = listCreate ( ) ;
selector - > channels = listCreate ( ) ;
selector - > allowed_firstargs = NULL ;
2022-11-03 10:14:56 -07:00
selector - > command_rules = sdsempty ( ) ;
2022-01-20 13:05:27 -08:00
listSetMatchMethod ( selector - > patterns , ACLListMatchKeyPattern ) ;
listSetFreeMethod ( selector - > patterns , ACLListFreeKeyPattern ) ;
listSetDupMethod ( selector - > patterns , ACLListDupKeyPattern ) ;
listSetMatchMethod ( selector - > channels , ACLListMatchSds ) ;
listSetFreeMethod ( selector - > channels , ACLListFreeSds ) ;
listSetDupMethod ( selector - > channels , ACLListDupSds ) ;
memset ( selector - > allowed_commands , 0 , sizeof ( selector - > allowed_commands ) ) ;
return selector ;
}
/* Cleanup the provided selector, including all interior structures. */
void ACLFreeSelector ( aclSelector * selector ) {
listRelease ( selector - > patterns ) ;
listRelease ( selector - > channels ) ;
2022-11-03 10:14:56 -07:00
sdsfree ( selector - > command_rules ) ;
2022-01-20 13:05:27 -08:00
ACLResetFirstArgs ( selector ) ;
zfree ( selector ) ;
}
/* Create an exact copy of the provided selector. */
aclSelector * ACLCopySelector ( aclSelector * src ) {
aclSelector * dst = zmalloc ( sizeof ( aclSelector ) ) ;
dst - > flags = src - > flags ;
dst - > patterns = listDup ( src - > patterns ) ;
dst - > channels = listDup ( src - > channels ) ;
2022-11-03 10:14:56 -07:00
dst - > command_rules = sdsdup ( src - > command_rules ) ;
2022-01-20 13:05:27 -08:00
memcpy ( dst - > allowed_commands , src - > allowed_commands ,
sizeof ( dst - > allowed_commands ) ) ;
dst - > allowed_firstargs = NULL ;
/* Copy the allowed first-args array of array of SDS strings. */
if ( src - > allowed_firstargs ) {
for ( int j = 0 ; j < USER_COMMAND_BITS_COUNT ; j + + ) {
if ( ! ( src - > allowed_firstargs [ j ] ) ) continue ;
for ( int i = 0 ; src - > allowed_firstargs [ j ] [ i ] ; i + + ) {
ACLAddAllowedFirstArg ( dst , j , src - > allowed_firstargs [ j ] [ i ] ) ;
}
}
}
return dst ;
}
/* List method for freeing a selector */
void ACLListFreeSelector ( void * a ) {
ACLFreeSelector ( ( aclSelector * ) a ) ;
}
/* List method for duplicating a selector */
void * ACLListDuplicateSelector ( void * src ) {
return ACLCopySelector ( ( aclSelector * ) src ) ;
}
/* All users have an implicit root selector which
* provides backwards compatibility to the old ACLs -
* permissions . */
aclSelector * ACLUserGetRootSelector ( user * u ) {
serverAssert ( listLength ( u - > selectors ) ) ;
aclSelector * s = ( aclSelector * ) listNodeValue ( listFirst ( u - > selectors ) ) ;
serverAssert ( s - > flags & SELECTOR_FLAG_ROOT ) ;
return s ;
}
2019-01-10 17:01:12 +01:00
/* Create a new user with the specified name, store it in the list
* of users ( the Users global radix tree ) , and returns a reference to
* the structure representing the user .
*
* If the user with such name already exists NULL is returned . */
2019-01-11 11:02:55 +01:00
user * ACLCreateUser ( const char * name , size_t namelen ) {
2023-12-14 17:50:18 -05:00
if ( raxFind ( Users , ( unsigned char * ) name , namelen , NULL ) ) return NULL ;
2019-01-10 17:01:12 +01:00
user * u = zmalloc ( sizeof ( * u ) ) ;
2019-01-15 18:16:20 +01:00
u - > name = sdsnewlen ( name , namelen ) ;
2022-01-20 13:05:27 -08:00
u - > flags = USER_FLAG_DISABLED ;
2022-09-22 14:13:39 +08:00
u - > flags | = USER_FLAG_SANITIZE_PAYLOAD ;
2019-01-10 17:01:12 +01:00
u - > passwords = listCreate ( ) ;
2022-09-22 16:29:00 +03:00
u - > acl_string = NULL ;
2019-01-15 12:58:54 +01:00
listSetMatchMethod ( u - > passwords , ACLListMatchSds ) ;
2019-01-28 12:11:11 +01:00
listSetFreeMethod ( u - > passwords , ACLListFreeSds ) ;
2019-02-07 12:57:21 +01:00
listSetDupMethod ( u - > passwords , ACLListDupSds ) ;
2022-01-20 13:05:27 -08:00
u - > selectors = listCreate ( ) ;
listSetFreeMethod ( u - > selectors , ACLListFreeSelector ) ;
listSetDupMethod ( u - > selectors , ACLListDuplicateSelector ) ;
/* Add the initial root selector */
aclSelector * s = ACLCreateSelector ( SELECTOR_FLAG_ROOT ) ;
listAddNodeHead ( u - > selectors , s ) ;
2019-01-10 17:01:12 +01:00
raxInsert ( Users , ( unsigned char * ) name , namelen , u , NULL ) ;
return u ;
}
2019-02-06 16:19:17 +01:00
/* This function should be called when we need an unlinked "fake" user
* we can use in order to validate ACL rules or for other similar reasons .
* The user will not get linked to the Users radix tree . The returned
* user should be released with ACLFreeUser ( ) as usually . */
user * ACLCreateUnlinkedUser ( void ) {
char username [ 64 ] ;
for ( int j = 0 ; ; j + + ) {
snprintf ( username , sizeof ( username ) , " __fakeuser:%d__ " , j ) ;
user * fakeuser = ACLCreateUser ( username , strlen ( username ) ) ;
if ( fakeuser = = NULL ) continue ;
int retval = raxRemove ( Users , ( unsigned char * ) username ,
strlen ( username ) , NULL ) ;
serverAssert ( retval ! = 0 ) ;
return fakeuser ;
}
}
2019-01-31 18:33:14 +01:00
/* Release the memory used by the user structure. Note that this function
* will not remove the user from the Users global radix tree . */
void ACLFreeUser ( user * u ) {
sdsfree ( u - > name ) ;
2022-09-22 16:29:00 +03:00
if ( u - > acl_string ) {
decrRefCount ( u - > acl_string ) ;
u - > acl_string = NULL ;
}
2019-01-31 18:33:14 +01:00
listRelease ( u - > passwords ) ;
2022-01-20 13:05:27 -08:00
listRelease ( u - > selectors ) ;
2019-01-31 18:33:14 +01:00
zfree ( u ) ;
}
2019-02-11 22:24:15 +08:00
/* When a user is deleted we need to cycle the active
* connections in order to kill all the pending ones that
* are authenticated with such user . */
void ACLFreeUserAndKillClients ( user * u ) {
listIter li ;
listNode * ln ;
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
client * c = listNodeValue ( ln ) ;
if ( c - > user = = u ) {
Squash merging 125 typo/grammar/comment/doc PRs (#7773)
List of squashed commits or PRs
===============================
commit 66801ea
Author: hwware <wen.hui.ware@gmail.com>
Date: Mon Jan 13 00:54:31 2020 -0500
typo fix in acl.c
commit 46f55db
Author: Itamar Haber <itamar@redislabs.com>
Date: Sun Sep 6 18:24:11 2020 +0300
Updates a couple of comments
Specifically:
* RM_AutoMemory completed instead of pointing to docs
* Updated link to custom type doc
commit 61a2aa0
Author: xindoo <xindoo@qq.com>
Date: Tue Sep 1 19:24:59 2020 +0800
Correct errors in code comments
commit a5871d1
Author: yz1509 <pro-756@qq.com>
Date: Tue Sep 1 18:36:06 2020 +0800
fix typos in module.c
commit 41eede7
Author: bookug <bookug@qq.com>
Date: Sat Aug 15 01:11:33 2020 +0800
docs: fix typos in comments
commit c303c84
Author: lazy-snail <ws.niu@outlook.com>
Date: Fri Aug 7 11:15:44 2020 +0800
fix spelling in redis.conf
commit 1eb76bf
Author: zhujian <zhujianxyz@gmail.com>
Date: Thu Aug 6 15:22:10 2020 +0800
add a missing 'n' in comment
commit 1530ec2
Author: Daniel Dai <764122422@qq.com>
Date: Mon Jul 27 00:46:35 2020 -0400
fix spelling in tracking.c
commit e517b31
Author: Hunter-Chen <huntcool001@gmail.com>
Date: Fri Jul 17 22:33:32 2020 +0800
Update redis.conf
Co-authored-by: Itamar Haber <itamar@redislabs.com>
commit c300eff
Author: Hunter-Chen <huntcool001@gmail.com>
Date: Fri Jul 17 22:33:23 2020 +0800
Update redis.conf
Co-authored-by: Itamar Haber <itamar@redislabs.com>
commit 4c058a8
Author: 陈浩鹏 <chenhaopeng@heytea.com>
Date: Thu Jun 25 19:00:56 2020 +0800
Grammar fix and clarification
commit 5fcaa81
Author: bodong.ybd <bodong.ybd@alibaba-inc.com>
Date: Fri Jun 19 10:09:00 2020 +0800
Fix typos
commit 4caca9a
Author: Pruthvi P <pruthvi@ixigo.com>
Date: Fri May 22 00:33:22 2020 +0530
Fix typo eviciton => eviction
commit b2a25f6
Author: Brad Dunbar <dunbarb2@gmail.com>
Date: Sun May 17 12:39:59 2020 -0400
Fix a typo.
commit 12842ae
Author: hwware <wen.hui.ware@gmail.com>
Date: Sun May 3 17:16:59 2020 -0400
fix spelling in redis conf
commit ddba07c
Author: Chris Lamb <chris@chris-lamb.co.uk>
Date: Sat May 2 23:25:34 2020 +0100
Correct a "conflicts" spelling error.
commit 8fc7bf2
Author: Nao YONASHIRO <yonashiro@r.recruit.co.jp>
Date: Thu Apr 30 10:25:27 2020 +0900
docs: fix EXPIRE_FAST_CYCLE_DURATION to ACTIVE_EXPIRE_CYCLE_FAST_DURATION
commit 9b2b67a
Author: Brad Dunbar <dunbarb2@gmail.com>
Date: Fri Apr 24 11:46:22 2020 -0400
Fix a typo.
commit 0746f10
Author: devilinrust <63737265+devilinrust@users.noreply.github.com>
Date: Thu Apr 16 00:17:53 2020 +0200
Fix typos in server.c
commit 92b588d
Author: benjessop12 <56115861+benjessop12@users.noreply.github.com>
Date: Mon Apr 13 13:43:55 2020 +0100
Fix spelling mistake in lazyfree.c
commit 1da37aa
Merge: 2d4ba28 af347a8
Author: hwware <wen.hui.ware@gmail.com>
Date: Thu Mar 5 22:41:31 2020 -0500
Merge remote-tracking branch 'upstream/unstable' into expiretypofix
commit 2d4ba28
Author: hwware <wen.hui.ware@gmail.com>
Date: Mon Mar 2 00:09:40 2020 -0500
fix typo in expire.c
commit 1a746f7
Author: SennoYuki <minakami1yuki@gmail.com>
Date: Thu Feb 27 16:54:32 2020 +0800
fix typo
commit 8599b1a
Author: dongheejeong <donghee950403@gmail.com>
Date: Sun Feb 16 20:31:43 2020 +0000
Fix typo in server.c
commit f38d4e8
Author: hwware <wen.hui.ware@gmail.com>
Date: Sun Feb 2 22:58:38 2020 -0500
fix typo in evict.c
commit fe143fc
Author: Leo Murillo <leonardo.murillo@gmail.com>
Date: Sun Feb 2 01:57:22 2020 -0600
Fix a few typos in redis.conf
commit 1ab4d21
Author: viraja1 <anchan.viraj@gmail.com>
Date: Fri Dec 27 17:15:58 2019 +0530
Fix typo in Latency API docstring
commit ca1f70e
Author: gosth <danxuedexing@qq.com>
Date: Wed Dec 18 15:18:02 2019 +0800
fix typo in sort.c
commit a57c06b
Author: ZYunH <zyunhjob@163.com>
Date: Mon Dec 16 22:28:46 2019 +0800
fix-zset-typo
commit b8c92b5
Author: git-hulk <hulk.website@gmail.com>
Date: Mon Dec 16 15:51:42 2019 +0800
FIX: typo in cluster.c, onformation->information
commit 9dd981c
Author: wujm2007 <jim.wujm@gmail.com>
Date: Mon Dec 16 09:37:52 2019 +0800
Fix typo
commit e132d7a
Author: Sebastien Williams-Wynn <s.williamswynn.mail@gmail.com>
Date: Fri Nov 15 00:14:07 2019 +0000
Minor typo change
commit 47f44d5
Author: happynote3966 <01ssrmikururudevice01@gmail.com>
Date: Mon Nov 11 22:08:48 2019 +0900
fix comment typo in redis-cli.c
commit b8bdb0d
Author: fulei <fulei@kuaishou.com>
Date: Wed Oct 16 18:00:17 2019 +0800
Fix a spelling mistake of comments in defragDictBucketCallback
commit 0def46a
Author: fulei <fulei@kuaishou.com>
Date: Wed Oct 16 13:09:27 2019 +0800
fix some spelling mistakes of comments in defrag.c
commit f3596fd
Author: Phil Rajchgot <tophil@outlook.com>
Date: Sun Oct 13 02:02:32 2019 -0400
Typo and grammar fixes
Redis and its documentation are great -- just wanted to submit a few corrections in the spirit of Hacktoberfest. Thanks for all your work on this project. I use it all the time and it works beautifully.
commit 2b928cd
Author: KangZhiDong <worldkzd@gmail.com>
Date: Sun Sep 1 07:03:11 2019 +0800
fix typos
commit 33aea14
Author: Axlgrep <axlgrep@gmail.com>
Date: Tue Aug 27 11:02:18 2019 +0800
Fixed eviction spelling issues
commit e282a80
Author: Simen Flatby <simen@oms.no>
Date: Tue Aug 20 15:25:51 2019 +0200
Update comments to reflect prop name
In the comments the prop is referenced as replica-validity-factor,
but it is really named cluster-replica-validity-factor.
commit 74d1f9a
Author: Jim Green <jimgreen2013@qq.com>
Date: Tue Aug 20 20:00:31 2019 +0800
fix comment error, the code is ok
commit eea1407
Author: Liao Tonglang <liaotonglang@gmail.com>
Date: Fri May 31 10:16:18 2019 +0800
typo fix
fix cna't to can't
commit 0da553c
Author: KAWACHI Takashi <tkawachi@gmail.com>
Date: Wed Jul 17 00:38:16 2019 +0900
Fix typo
commit 7fc8fb6
Author: Michael Prokop <mika@grml.org>
Date: Tue May 28 17:58:42 2019 +0200
Typo fixes
s/familar/familiar/
s/compatiblity/compatibility/
s/ ot / to /
s/itsef/itself/
commit 5f46c9d
Author: zhumoing <34539422+zhumoing@users.noreply.github.com>
Date: Tue May 21 21:16:50 2019 +0800
typo-fixes
typo-fixes
commit 321dfe1
Author: wxisme <850885154@qq.com>
Date: Sat Mar 16 15:10:55 2019 +0800
typo fix
commit b4fb131
Merge: 267e0e6 3df1eb8
Author: Nikitas Bastas <nikitasbst@gmail.com>
Date: Fri Feb 8 22:55:45 2019 +0200
Merge branch 'unstable' of antirez/redis into unstable
commit 267e0e6
Author: Nikitas Bastas <nikitasbst@gmail.com>
Date: Wed Jan 30 21:26:04 2019 +0200
Minor typo fix
commit 30544e7
Author: inshal96 <39904558+inshal96@users.noreply.github.com>
Date: Fri Jan 4 16:54:50 2019 +0500
remove an extra 'a' in the comments
commit 337969d
Author: BrotherGao <yangdongheng11@gmail.com>
Date: Sat Dec 29 12:37:29 2018 +0800
fix typo in redis.conf
commit 9f4b121
Merge: 423a030 e504583
Author: BrotherGao <yangdongheng@xiaomi.com>
Date: Sat Dec 29 11:41:12 2018 +0800
Merge branch 'unstable' of antirez/redis into unstable
commit 423a030
Merge: 42b02b7 46a51cd
Author: 杨东衡 <yangdongheng@xiaomi.com>
Date: Tue Dec 4 23:56:11 2018 +0800
Merge branch 'unstable' of antirez/redis into unstable
commit 42b02b7
Merge: 68c0e6e b8febe6
Author: Dongheng Yang <yangdongheng11@gmail.com>
Date: Sun Oct 28 15:54:23 2018 +0800
Merge pull request #1 from antirez/unstable
update local data
commit 714b589
Author: Christian <crifei93@gmail.com>
Date: Fri Dec 28 01:17:26 2018 +0100
fix typo "resulution"
commit e23259d
Author: garenchan <1412950785@qq.com>
Date: Wed Dec 26 09:58:35 2018 +0800
fix typo: segfauls -> segfault
commit a9359f8
Author: xjp <jianping_xie@aliyun.com>
Date: Tue Dec 18 17:31:44 2018 +0800
Fixed REDISMODULE_H spell bug
commit a12c3e4
Author: jdiaz <jrd.palacios@gmail.com>
Date: Sat Dec 15 23:39:52 2018 -0600
Fixes hyperloglog hash function comment block description
commit 770eb11
Author: 林上耀 <1210tom@163.com>
Date: Sun Nov 25 17:16:10 2018 +0800
fix typo
commit fd97fbb
Author: Chris Lamb <chris@chris-lamb.co.uk>
Date: Fri Nov 23 17:14:01 2018 +0100
Correct "unsupported" typo.
commit a85522d
Author: Jungnam Lee <jungnam.lee@oracle.com>
Date: Thu Nov 8 23:01:29 2018 +0900
fix typo in test comments
commit ade8007
Author: Arun Kumar <palerdot@users.noreply.github.com>
Date: Tue Oct 23 16:56:35 2018 +0530
Fixed grammatical typo
Fixed typo for word 'dictionary'
commit 869ee39
Author: Hamid Alaei <hamid.a85@gmail.com>
Date: Sun Aug 12 16:40:02 2018 +0430
fix documentations: (ThreadSafeContextStart/Stop -> ThreadSafeContextLock/Unlock), minor typo
commit f89d158
Author: Mayank Jain <mayankjain255@gmail.com>
Date: Tue Jul 31 23:01:21 2018 +0530
Updated README.md with some spelling corrections.
Made correction in spelling of some misspelled words.
commit 892198e
Author: dsomeshwar <someshwar.dhayalan@gmail.com>
Date: Sat Jul 21 23:23:04 2018 +0530
typo fix
commit 8a4d780
Author: Itamar Haber <itamar@redislabs.com>
Date: Mon Apr 30 02:06:52 2018 +0300
Fixes some typos
commit e3acef6
Author: Noah Rosamilia <ivoahivoah@gmail.com>
Date: Sat Mar 3 23:41:21 2018 -0500
Fix typo in /deps/README.md
commit 04442fb
Author: WuYunlong <xzsyeb@126.com>
Date: Sat Mar 3 10:32:42 2018 +0800
Fix typo in readSyncBulkPayload() comment.
commit 9f36880
Author: WuYunlong <xzsyeb@126.com>
Date: Sat Mar 3 10:20:37 2018 +0800
replication.c comment: run_id -> replid.
commit f866b4a
Author: Francesco 'makevoid' Canessa <makevoid@gmail.com>
Date: Thu Feb 22 22:01:56 2018 +0000
fix comment typo in server.c
commit 0ebc69b
Author: 줍 <jubee0124@gmail.com>
Date: Mon Feb 12 16:38:48 2018 +0900
Fix typo in redis.conf
Fix `five behaviors` to `eight behaviors` in [this sentence ](antirez/redis@unstable/redis.conf#L564)
commit b50a620
Author: martinbroadhurst <martinbroadhurst@users.noreply.github.com>
Date: Thu Dec 28 12:07:30 2017 +0000
Fix typo in valgrind.sup
commit 7d8f349
Author: Peter Boughton <peter@sorcerersisle.com>
Date: Mon Nov 27 19:52:19 2017 +0000
Update CONTRIBUTING; refer doc updates to redis-doc repo.
commit 02dec7e
Author: Klauswk <klauswk1@hotmail.com>
Date: Tue Oct 24 16:18:38 2017 -0200
Fix typo in comment
commit e1efbc8
Author: chenshi <baiwfg2@gmail.com>
Date: Tue Oct 3 18:26:30 2017 +0800
Correct two spelling errors of comments
commit 93327d8
Author: spacewander <spacewanderlzx@gmail.com>
Date: Wed Sep 13 16:47:24 2017 +0800
Update the comment for OBJ_ENCODING_EMBSTR_SIZE_LIMIT's value
The value of OBJ_ENCODING_EMBSTR_SIZE_LIMIT is 44 now instead of 39.
commit 63d361f
Author: spacewander <spacewanderlzx@gmail.com>
Date: Tue Sep 12 15:06:42 2017 +0800
Fix <prevlen> related doc in ziplist.c
According to the definition of ZIP_BIG_PREVLEN and other related code,
the guard of single byte <prevlen> should be 254 instead of 255.
commit ebe228d
Author: hanael80 <hanael80@gmail.com>
Date: Tue Aug 15 09:09:40 2017 +0900
Fix typo
commit 6b696e6
Author: Matt Robenolt <matt@ydekproductions.com>
Date: Mon Aug 14 14:50:47 2017 -0700
Fix typo in LATENCY DOCTOR output
commit a2ec6ae
Author: caosiyang <caosiyang@qiyi.com>
Date: Tue Aug 15 14:15:16 2017 +0800
Fix a typo: form => from
commit 3ab7699
Author: caosiyang <caosiyang@qiyi.com>
Date: Thu Aug 10 18:40:33 2017 +0800
Fix a typo: replicationFeedSlavesFromMaster() => replicationFeedSlavesFromMasterStream()
commit 72d43ef
Author: caosiyang <caosiyang@qiyi.com>
Date: Tue Aug 8 15:57:25 2017 +0800
fix a typo: servewr => server
commit 707c958
Author: Bo Cai <charpty@gmail.com>
Date: Wed Jul 26 21:49:42 2017 +0800
redis-cli.c typo: conut -> count.
Signed-off-by: Bo Cai <charpty@gmail.com>
commit b9385b2
Author: JackDrogon <jack.xsuperman@gmail.com>
Date: Fri Jun 30 14:22:31 2017 +0800
Fix some spell problems
commit 20d9230
Author: akosel <aaronjkosel@gmail.com>
Date: Sun Jun 4 19:35:13 2017 -0500
Fix typo
commit b167bfc
Author: Krzysiek Witkowicz <krzysiekwitkowicz@gmail.com>
Date: Mon May 22 21:32:27 2017 +0100
Fix #4008 small typo in comment
commit 2b78ac8
Author: Jake Clarkson <jacobwclarkson@gmail.com>
Date: Wed Apr 26 15:49:50 2017 +0100
Correct typo in tests/unit/hyperloglog.tcl
commit b0f1cdb
Author: Qi Luo <qiluo-msft@users.noreply.github.com>
Date: Wed Apr 19 14:25:18 2017 -0700
Fix typo
commit a90b0f9
Author: charsyam <charsyam@naver.com>
Date: Thu Mar 16 18:19:53 2017 +0900
fix typos
fix typos
fix typos
commit 8430a79
Author: Richard Hart <richardhart92@gmail.com>
Date: Mon Mar 13 22:17:41 2017 -0400
Fixed log message typo in listenToPort.
commit 481a1c2
Author: Vinod Kumar <kumar003vinod@gmail.com>
Date: Sun Jan 15 23:04:51 2017 +0530
src/db.c: Correct "save" -> "safe" typo
commit 586b4d3
Author: wangshaonan <wshn13@gmail.com>
Date: Wed Dec 21 20:28:27 2016 +0800
Fix typo they->the in helloworld.c
commit c1c4b5e
Author: Jenner <hypxm@qq.com>
Date: Mon Dec 19 16:39:46 2016 +0800
typo error
commit 1ee1a3f
Author: tielei <43289893@qq.com>
Date: Mon Jul 18 13:52:25 2016 +0800
fix some comments
commit 11a41fb
Author: Otto Kekäläinen <otto@seravo.fi>
Date: Sun Jul 3 10:23:55 2016 +0100
Fix spelling in documentation and comments
commit 5fb5d82
Author: francischan <f1ancis621@gmail.com>
Date: Tue Jun 28 00:19:33 2016 +0800
Fix outdated comments about redis.c file.
It should now refer to server.c file.
commit 6b254bc
Author: lmatt-bit <lmatt123n@gmail.com>
Date: Thu Apr 21 21:45:58 2016 +0800
Refine the comment of dictRehashMilliseconds func
SLAVECONF->REPLCONF in comment - by andyli029
commit ee9869f
Author: clark.kang <charsyam@naver.com>
Date: Tue Mar 22 11:09:51 2016 +0900
fix typos
commit f7b3b11
Author: Harisankar H <harisankarh@gmail.com>
Date: Wed Mar 9 11:49:42 2016 +0530
Typo correction: "faield" --> "failed"
Typo correction: "faield" --> "failed"
commit 3fd40fc
Author: Itamar Haber <itamar@redislabs.com>
Date: Thu Feb 25 10:31:51 2016 +0200
Fixes a typo in comments
commit 621c160
Author: Prayag Verma <prayag.verma@gmail.com>
Date: Mon Feb 1 12:36:20 2016 +0530
Fix typo in Readme.md
Spelling mistakes -
`eviciton` > `eviction`
`familar` > `familiar`
commit d7d07d6
Author: WonCheol Lee <toctoc21c@gmail.com>
Date: Wed Dec 30 15:11:34 2015 +0900
Typo fixed
commit a4dade7
Author: Felix Bünemann <buenemann@louis.info>
Date: Mon Dec 28 11:02:55 2015 +0100
[ci skip] Improve supervised upstart config docs
This mentions that "expect stop" is required for supervised upstart
to work correctly. See http://upstart.ubuntu.com/cookbook/#expect-stop
for an explanation.
commit d9caba9
Author: daurnimator <quae@daurnimator.com>
Date: Mon Dec 21 18:30:03 2015 +1100
README: Remove trailing whitespace
commit 72d42e5
Author: daurnimator <quae@daurnimator.com>
Date: Mon Dec 21 18:29:32 2015 +1100
README: Fix typo. th => the
commit dd6e957
Author: daurnimator <quae@daurnimator.com>
Date: Mon Dec 21 18:29:20 2015 +1100
README: Fix typo. familar => familiar
commit 3a12b23
Author: daurnimator <quae@daurnimator.com>
Date: Mon Dec 21 18:28:54 2015 +1100
README: Fix typo. eviciton => eviction
commit 2d1d03b
Author: daurnimator <quae@daurnimator.com>
Date: Mon Dec 21 18:21:45 2015 +1100
README: Fix typo. sever => server
commit 3973b06
Author: Itamar Haber <itamar@garantiadata.com>
Date: Sat Dec 19 17:01:20 2015 +0200
Typo fix
commit 4f2e460
Author: Steve Gao <fu@2token.com>
Date: Fri Dec 4 10:22:05 2015 +0800
Update README - fix typos
commit b21667c
Author: binyan <binbin.yan@nokia.com>
Date: Wed Dec 2 22:48:37 2015 +0800
delete redundancy color judge in sdscatcolor
commit 88894c7
Author: binyan <binbin.yan@nokia.com>
Date: Wed Dec 2 22:14:42 2015 +0800
the example output shoule be HelloWorld
commit 2763470
Author: binyan <binbin.yan@nokia.com>
Date: Wed Dec 2 17:41:39 2015 +0800
modify error word keyevente
Signed-off-by: binyan <binbin.yan@nokia.com>
commit 0847b3d
Author: Bruno Martins <bscmartins@gmail.com>
Date: Wed Nov 4 11:37:01 2015 +0000
typo
commit bbb9e9e
Author: dawedawe <dawedawe@gmx.de>
Date: Fri Mar 27 00:46:41 2015 +0100
typo: zimap -> zipmap
commit 5ed297e
Author: Axel Advento <badwolf.bloodseeker.rev@gmail.com>
Date: Tue Mar 3 15:58:29 2015 +0800
Fix 'salve' typos to 'slave'
commit edec9d6
Author: LudwikJaniuk <ludvig.janiuk@gmail.com>
Date: Wed Jun 12 14:12:47 2019 +0200
Update README.md
Co-Authored-By: Qix <Qix-@users.noreply.github.com>
commit 692a7af
Author: LudwikJaniuk <ludvig.janiuk@gmail.com>
Date: Tue May 28 14:32:04 2019 +0200
grammar
commit d962b0a
Author: Nick Frost <nickfrostatx@gmail.com>
Date: Wed Jul 20 15:17:12 2016 -0700
Minor grammar fix
commit 24fff01aaccaf5956973ada8c50ceb1462e211c6 (typos)
Author: Chad Miller <chadm@squareup.com>
Date: Tue Sep 8 13:46:11 2020 -0400
Fix faulty comment about operation of unlink()
commit 3cd5c1f3326c52aa552ada7ec797c6bb16452355
Author: Kevin <kevin.xgr@gmail.com>
Date: Wed Nov 20 00:13:50 2019 +0800
Fix typo in server.c.
From a83af59 Mon Sep 17 00:00:00 2001
From: wuwo <wuwo@wacai.com>
Date: Fri, 17 Mar 2017 20:37:45 +0800
Subject: [PATCH] falure to failure
From c961896 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B7=A6=E6=87=B6?= <veficos@gmail.com>
Date: Sat, 27 May 2017 15:33:04 +0800
Subject: [PATCH] fix typo
From e600ef2 Mon Sep 17 00:00:00 2001
From: "rui.zou" <rui.zou@yunify.com>
Date: Sat, 30 Sep 2017 12:38:15 +0800
Subject: [PATCH] fix a typo
From c7d07fa Mon Sep 17 00:00:00 2001
From: Alexandre Perrin <alex@kaworu.ch>
Date: Thu, 16 Aug 2018 10:35:31 +0200
Subject: [PATCH] deps README.md typo
From b25cb67 Mon Sep 17 00:00:00 2001
From: Guy Korland <gkorland@gmail.com>
Date: Wed, 26 Sep 2018 10:55:37 +0300
Subject: [PATCH 1/2] fix typos in header
From ad28ca6 Mon Sep 17 00:00:00 2001
From: Guy Korland <gkorland@gmail.com>
Date: Wed, 26 Sep 2018 11:02:36 +0300
Subject: [PATCH 2/2] fix typos
commit 34924cdedd8552466fc22c1168d49236cb7ee915
Author: Adrian Lynch <adi_ady_ade@hotmail.com>
Date: Sat Apr 4 21:59:15 2015 +0100
Typos fixed
commit fd2a1e7
Author: Jan <jsteemann@users.noreply.github.com>
Date: Sat Oct 27 19:13:01 2018 +0200
Fix typos
Fix typos
commit e14e47c1a234b53b0e103c5f6a1c61481cbcbb02
Author: Andy Lester <andy@petdance.com>
Date: Fri Aug 2 22:30:07 2019 -0500
Fix multiple misspellings of "following"
commit 79b948ce2dac6b453fe80995abbcaac04c213d5a
Author: Andy Lester <andy@petdance.com>
Date: Fri Aug 2 22:24:28 2019 -0500
Fix misspelling of create-cluster
commit 1fffde52666dc99ab35efbd31071a4c008cb5a71
Author: Andy Lester <andy@petdance.com>
Date: Wed Jul 31 17:57:56 2019 -0500
Fix typos
commit 204c9ba9651e9e05fd73936b452b9a30be456cfe
Author: Xiaobo Zhu <xiaobo.zhu@shopee.com>
Date: Tue Aug 13 22:19:25 2019 +0800
fix typos
Squashed commit of the following:
commit 1d9aaf8
Author: danmedani <danmedani@gmail.com>
Date: Sun Aug 2 11:40:26 2015 -0700
README typo fix.
Squashed commit of the following:
commit 32bfa7c
Author: Erik Dubbelboer <erik@dubbelboer.com>
Date: Mon Jul 6 21:15:08 2015 +0200
Fixed grammer
Squashed commit of the following:
commit b24f69c
Author: Sisir Koppaka <sisir.koppaka@gmail.com>
Date: Mon Mar 2 22:38:45 2015 -0500
utils/hashtable/rehashing.c: Fix typos
Squashed commit of the following:
commit 4e04082
Author: Erik Dubbelboer <erik@dubbelboer.com>
Date: Mon Mar 23 08:22:21 2015 +0000
Small config file documentation improvements
Squashed commit of the following:
commit acb8773
Author: ctd1500 <ctd1500@gmail.com>
Date: Fri May 8 01:52:48 2015 -0700
Typo and grammar fixes in readme
commit 2eb75b6
Author: ctd1500 <ctd1500@gmail.com>
Date: Fri May 8 01:36:18 2015 -0700
fixed redis.conf comment
Squashed commit of the following:
commit a8249a2
Author: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Fri Dec 11 11:39:52 2015 +0530
Revise correction of typos.
Squashed commit of the following:
commit 3c02028
Author: zhaojun11 <zhaojun11@jd.com>
Date: Wed Jan 17 19:05:28 2018 +0800
Fix typos include two code typos in cluster.c and latency.c
Squashed commit of the following:
commit 9dba47c
Author: q191201771 <191201771@qq.com>
Date: Sat Jan 4 11:31:04 2020 +0800
fix function listCreate comment in adlist.c
Update src/server.c
commit 2c7c2cb536e78dd211b1ac6f7bda00f0f54faaeb
Author: charpty <charpty@gmail.com>
Date: Tue May 1 23:16:59 2018 +0800
server.c typo: modules system dictionary type comment
Signed-off-by: charpty <charpty@gmail.com>
commit a8395323fb63cb59cb3591cb0f0c8edb7c29a680
Author: Itamar Haber <itamar@redislabs.com>
Date: Sun May 6 00:25:18 2018 +0300
Updates test_helper.tcl's help with undocumented options
Specifically:
* Host
* Port
* Client
commit bde6f9ced15755cd6407b4af7d601b030f36d60b
Author: wxisme <850885154@qq.com>
Date: Wed Aug 8 15:19:19 2018 +0800
fix comments in deps files
commit 3172474ba991532ab799ee1873439f3402412331
Author: wxisme <850885154@qq.com>
Date: Wed Aug 8 14:33:49 2018 +0800
fix some comments
commit 01b6f2b6858b5cf2ce4ad5092d2c746e755f53f0
Author: Thor Juhasz <thor@juhasz.pro>
Date: Sun Nov 18 14:37:41 2018 +0100
Minor fixes to comments
Found some parts a little unclear on a first read, which prompted me to have a better look at the file and fix some minor things I noticed.
Fixing minor typos and grammar. There are no changes to configuration options.
These changes are only meant to help the user better understand the explanations to the various configuration options
2020-09-10 13:43:38 +03:00
/* We'll free the connection asynchronously, so
2019-02-11 22:24:15 +08:00
* in theory to set a different user is not needed .
2024-04-09 01:24:03 -07:00
* However if there are bugs in the server , soon or later
2019-02-11 22:24:15 +08:00
* this may result in some security hole : it ' s much
* more defensive to set the default user and put
* it in non authenticated mode . */
c - > user = DefaultUser ;
c - > authenticated = 0 ;
Don't write replies if close the client ASAP (#7202)
Before this commit, we would have continued to add replies to the reply buffer even if client
output buffer limit is reached, so the used memory would keep increasing over the configured limit.
What's more, we shouldn’t write any reply to the client if it is set 'CLIENT_CLOSE_ASAP' flag
because that doesn't conform to its definition and we will close all clients flagged with
'CLIENT_CLOSE_ASAP' in ‘beforeSleep’.
Because of code execution order, before this, we may firstly write to part of the replies to
the socket before disconnecting it, but in fact, we may can’t send the full replies to clients
since OS socket buffer is limited. But this unexpected behavior makes some commands work well,
for instance ACL DELUSER, if the client deletes the current user, we need to send reply to client
and close the connection, but before, we close the client firstly and write the reply to reply
buffer. secondly, we shouldn't do this despite the fact it works well in most cases.
We add a flag 'CLIENT_CLOSE_AFTER_COMMAND' to mark clients, this flag means we will close the
client after executing commands and send all entire replies, so that we can write replies to
reply buffer during executing commands, send replies to clients, and close them later.
We also fix some implicit problems. If client output buffer limit is enforced in 'multi/exec',
all commands will be executed completely in redis and clients will not read any reply instead of
partial replies. Even more, if the client executes 'ACL deluser' the using user in 'multi/exec',
it will not read the replies after 'ACL deluser' just like before executing 'client kill' itself
in 'multi/exec'.
We added some tests for output buffer limit breach during multi-exec and using a pipeline of
many small commands rather than one with big response.
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-09-24 21:01:41 +08:00
/* We will write replies to this client later, so we can't
* close it directly even if async . */
if ( c = = server . current_client ) {
c - > flags | = CLIENT_CLOSE_AFTER_COMMAND ;
} else {
freeClientAsync ( c ) ;
}
2019-02-11 22:24:15 +08:00
}
}
2019-02-11 16:28:31 +01:00
ACLFreeUser ( u ) ;
2019-02-11 22:24:15 +08:00
}
2019-02-07 12:57:21 +01:00
/* Copy the user ACL rules from the source user 'src' to the destination
* user ' dst ' so that at the end of the process they ' ll have exactly the
* same rules ( but the names will continue to be the original ones ) . */
void ACLCopyUser ( user * dst , user * src ) {
listRelease ( dst - > passwords ) ;
2022-01-20 13:05:27 -08:00
listRelease ( dst - > selectors ) ;
2019-02-07 12:57:21 +01:00
dst - > passwords = listDup ( src - > passwords ) ;
2022-01-20 13:05:27 -08:00
dst - > selectors = listDup ( src - > selectors ) ;
2019-02-07 12:57:21 +01:00
dst - > flags = src - > flags ;
2022-09-22 16:29:00 +03:00
if ( dst - > acl_string ) {
decrRefCount ( dst - > acl_string ) ;
}
dst - > acl_string = src - > acl_string ;
if ( dst - > acl_string ) {
/* if src is NULL, we set it to NULL, if not, need to increment reference count */
incrRefCount ( dst - > acl_string ) ;
}
2019-02-07 12:57:21 +01:00
}
2019-01-18 18:16:03 +01:00
/* Given a command ID, this function set by reference 'word' and 'bit'
* so that user - > allowed_commands [ word ] will address the right word
* where the corresponding bit for the provided ID is stored , and
* so that user - > allowed_commands [ word ] & bit will identify that specific
* bit . The function returns C_ERR in case the specified ID overflows
* the bitmap in the user representation . */
2019-01-26 12:51:43 +01:00
int ACLGetCommandBitCoordinates ( uint64_t id , uint64_t * word , uint64_t * bit ) {
2019-01-23 08:10:57 +01:00
if ( id > = USER_COMMAND_BITS_COUNT ) return C_ERR ;
2019-01-18 18:16:03 +01:00
* word = id / sizeof ( uint64_t ) / 8 ;
2019-01-26 12:51:43 +01:00
* bit = 1ULL < < ( id % ( sizeof ( uint64_t ) * 8 ) ) ;
2019-01-18 18:16:03 +01:00
return C_OK ;
}
/* Check if the specified command bit is set for the specified user.
* The function returns 1 is the bit is set or 0 if it is not .
* Note that this function does not check the ALLCOMMANDS flag of the user
* but just the lowlevel bitmask .
*
2019-06-07 13:20:22 -07:00
* If the bit overflows the user internal representation , zero is returned
2019-01-18 18:16:03 +01:00
* in order to disallow the execution of the command in such edge case . */
2022-01-20 13:05:27 -08:00
int ACLGetSelectorCommandBit ( const aclSelector * selector , unsigned long id ) {
2019-01-18 18:16:03 +01:00
uint64_t word , bit ;
if ( ACLGetCommandBitCoordinates ( id , & word , & bit ) = = C_ERR ) return 0 ;
2022-01-20 13:05:27 -08:00
return ( selector - > allowed_commands [ word ] & bit ) ! = 0 ;
2019-01-18 18:16:03 +01:00
}
2019-01-23 08:14:56 +01:00
/* When +@all or allcommands is given, we set a reserved bit as well that we
* can later test , to see if the user has the right to execute " future commands " ,
* that is , commands loaded later via modules . */
2022-01-20 13:05:27 -08:00
int ACLSelectorCanExecuteFutureCommands ( aclSelector * selector ) {
return ACLGetSelectorCommandBit ( selector , USER_COMMAND_BITS_COUNT - 1 ) ;
2019-01-23 08:14:56 +01:00
}
2019-01-18 18:16:03 +01:00
/* Set the specified command bit for the specified user to 'value' (0 or 1).
2019-06-07 13:20:22 -07:00
* If the bit overflows the user internal representation , no operation
2019-01-25 13:27:33 +01:00
* is performed . As a side effect of calling this function with a value of
* zero , the user flag ALLCOMMANDS is cleared since it is no longer possible
* to skip the command bit explicit test . */
2022-01-20 13:05:27 -08:00
void ACLSetSelectorCommandBit ( aclSelector * selector , unsigned long id , int value ) {
2019-01-18 18:16:03 +01:00
uint64_t word , bit ;
if ( ACLGetCommandBitCoordinates ( id , & word , & bit ) = = C_ERR ) return ;
2020-05-19 00:58:58 +08:00
if ( value ) {
2022-01-20 13:05:27 -08:00
selector - > allowed_commands [ word ] | = bit ;
2020-05-19 00:58:58 +08:00
} else {
2022-01-20 13:05:27 -08:00
selector - > allowed_commands [ word ] & = ~ bit ;
selector - > flags & = ~ SELECTOR_FLAG_ALLCOMMANDS ;
2020-05-19 00:58:58 +08:00
}
2019-01-18 18:16:03 +01:00
}
2022-11-03 10:14:56 -07:00
/* Remove a rule from the retained command rules. Always match rules
* verbatim , but also remove subcommand rules if we are adding or removing the
* entire command . */
void ACLSelectorRemoveCommandRule ( aclSelector * selector , sds new_rule ) {
size_t new_len = sdslen ( new_rule ) ;
char * existing_rule = selector - > command_rules ;
/* Loop over the existing rules, trying to find a rule that "matches"
* the new rule . If we find a match , then remove the command from the string by
* copying the later rules over it . */
while ( existing_rule [ 0 ] ) {
/* The first character of the rule is +/-, which we don't need to compare. */
char * copy_position = existing_rule ;
existing_rule + = 1 ;
/* Assume a trailing space after a command is part of the command, like '+get ', so trim it
* as well if the command is removed . */
char * rule_end = strchr ( existing_rule , ' ' ) ;
if ( ! rule_end ) {
Fixed a bug where sequential matching ACL rules weren't compressed (#12472)
When adding a new ACL rule was added, an attempt was made to remove
any "overlapping" rules. However, there when a match was found, the search
was not resumed at the right location, but instead after the original position of
the original command.
For example, if the current rules were `-config +config|get` and a rule `+config`
was added. It would identify that `-config` was matched, but it would skip over
`+config|get`, leaving the compacted rule `-config +config`. This would be evaluated
safely, but looks weird.
This bug can only be triggered with subcommands, since that is the only way to
have sequential matching rules. Resolves #12470. This is also only present in 7.2.
I think there was also a minor risk of removing another valid rule, since it would start
the search of the next command at an arbitrary point. I couldn't find a valid offset that
would have cause a match using any of the existing commands that have subcommands
with another command.
2023-08-09 23:58:53 -07:00
/* This is the last rule, so move it to the end of the string. */
2022-11-03 10:14:56 -07:00
rule_end = existing_rule + strlen ( existing_rule ) ;
/* This approach can leave a trailing space if the last rule is removed,
* but only if it ' s not the first rule , so handle that case . */
if ( copy_position ! = selector - > command_rules ) copy_position - = 1 ;
}
char * copy_end = rule_end ;
if ( * copy_end = = ' ' ) copy_end + + ;
/* Exact match or the rule we are comparing is a subcommand denoted by '|' */
size_t existing_len = rule_end - existing_rule ;
if ( ! memcmp ( existing_rule , new_rule , min ( existing_len , new_len ) ) ) {
if ( ( existing_len = = new_len ) | | ( existing_len > new_len & & ( existing_rule [ new_len ] ) = = ' | ' ) ) {
/* Copy the remaining rules starting at the next rule to replace the rule to be
* deleted , including the terminating NULL character . */
memmove ( copy_position , copy_end , strlen ( copy_end ) + 1 ) ;
Fixed a bug where sequential matching ACL rules weren't compressed (#12472)
When adding a new ACL rule was added, an attempt was made to remove
any "overlapping" rules. However, there when a match was found, the search
was not resumed at the right location, but instead after the original position of
the original command.
For example, if the current rules were `-config +config|get` and a rule `+config`
was added. It would identify that `-config` was matched, but it would skip over
`+config|get`, leaving the compacted rule `-config +config`. This would be evaluated
safely, but looks weird.
This bug can only be triggered with subcommands, since that is the only way to
have sequential matching rules. Resolves #12470. This is also only present in 7.2.
I think there was also a minor risk of removing another valid rule, since it would start
the search of the next command at an arbitrary point. I couldn't find a valid offset that
would have cause a match using any of the existing commands that have subcommands
with another command.
2023-08-09 23:58:53 -07:00
existing_rule = copy_position ;
continue ;
2022-11-03 10:14:56 -07:00
}
}
existing_rule = copy_end ;
}
/* There is now extra padding at the end of the rules, so clean that up. */
sdsupdatelen ( selector - > command_rules ) ;
}
2024-03-31 13:11:13 +08:00
/* This function is responsible for updating the command_rules struct so that relative ordering of
2022-11-03 10:14:56 -07:00
* commands and categories is maintained and can be reproduced without loss . */
void ACLUpdateCommandRules ( aclSelector * selector , const char * rule , int allow ) {
sds new_rule = sdsnew ( rule ) ;
sdstolower ( new_rule ) ;
ACLSelectorRemoveCommandRule ( selector , new_rule ) ;
if ( sdslen ( selector - > command_rules ) ) selector - > command_rules = sdscat ( selector - > command_rules , " " ) ;
selector - > command_rules = sdscatfmt ( selector - > command_rules , allow ? " +%S " : " -%S " , new_rule ) ;
sdsfree ( new_rule ) ;
}
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
/* This function is used to allow/block a specific command.
* Allowing / blocking a container command also applies for its subcommands */
2024-04-03 23:54:33 +07:00
void ACLChangeSelectorPerm ( aclSelector * selector , struct serverCommand * cmd , int allow ) {
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
unsigned long id = cmd - > id ;
2022-01-20 13:05:27 -08:00
ACLSetSelectorCommandBit ( selector , id , allow ) ;
ACLResetFirstArgsForCommand ( selector , id ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
if ( cmd - > subcommands_dict ) {
dictEntry * de ;
dictIterator * di = dictGetSafeIterator ( cmd - > subcommands_dict ) ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * sub = ( struct serverCommand * ) dictGetVal ( de ) ;
2022-01-20 13:05:27 -08:00
ACLSetSelectorCommandBit ( selector , sub - > id , allow ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
}
2021-10-21 11:50:58 +02:00
dictReleaseIterator ( di ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
}
}
2022-11-03 10:14:56 -07:00
/* This is like ACLSetSelectorCommandBit(), but instead of setting the specified
* ID , it will check all the commands in the category specified as argument ,
* and will set all the bits corresponding to such commands to the specified
* value . Since the category passed by the user may be non existing , the
* function returns C_ERR if the category was not found , or C_OK if it was
* found and the operation was performed . */
void ACLSetSelectorCommandBitsForCategory ( dict * commands , aclSelector * selector , uint64_t cflag , int value ) {
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
dictIterator * di = dictGetIterator ( commands ) ;
dictEntry * de ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd = dictGetVal ( de ) ;
Auto-generate the command table from JSON files (#9656)
Delete the hardcoded command table and replace it with an auto-generated table, based
on a JSON file that describes the commands (each command must have a JSON file).
These JSON files are the SSOT of everything there is to know about Redis commands,
and it is reflected fully in COMMAND INFO.
These JSON files are used to generate commands.c (using a python script), which is then
committed to the repo and compiled.
The purpose is:
* Clients and proxies will be able to get much more info from redis, instead of relying on hard coded logic.
* drop the dependency between Redis-user and the commands.json in redis-doc.
* delete help.h and have redis-cli learn everything it needs to know just by issuing COMMAND (will be
done in a separate PR)
* redis.io should stop using commands.json and learn everything from Redis (ultimately one of the release
artifacts should be a large JSON, containing all the information about all of the commands, which will be
generated from COMMAND's reply)
* the byproduct of this is:
* module commands will be able to provide that info and possibly be more of a first-class citizens
* in theory, one may be able to generate a redis client library for a strictly typed language, by using this info.
### Interface changes
#### COMMAND INFO's reply change (and arg-less COMMAND)
Before this commit the reply at index 7 contained the key-specs list
and reply at index 8 contained the sub-commands list (Both unreleased).
Now, reply at index 7 is a map of:
- summary - short command description
- since - debut version
- group - command group
- complexity - complexity string
- doc-flags - flags used for documentation (e.g. "deprecated")
- deprecated-since - if deprecated, from which version?
- replaced-by - if deprecated, which command replaced it?
- history - a list of (version, what-changed) tuples
- hints - a list of strings, meant to provide hints for clients/proxies. see https://github.com/redis/redis/issues/9876
- arguments - an array of arguments. each element is a map, with the possibility of nesting (sub-arguments)
- key-specs - an array of keys specs (already in unstable, just changed location)
- subcommands - a list of sub-commands (already in unstable, just changed location)
- reply-schema - will be added in the future (see https://github.com/redis/redis/issues/9845)
more details on these can be found in https://github.com/redis/redis-doc/pull/1697
only the first three fields are mandatory
#### API changes (unreleased API obviously)
now they take RedisModuleCommand opaque pointer instead of looking up the command by name
- RM_CreateSubcommand
- RM_AddCommandKeySpec
- RM_SetCommandKeySpecBeginSearchIndex
- RM_SetCommandKeySpecBeginSearchKeyword
- RM_SetCommandKeySpecFindKeysRange
- RM_SetCommandKeySpecFindKeysKeynum
Currently, we did not add module API to provide additional information about their commands because
we couldn't agree on how the API should look like, see https://github.com/redis/redis/issues/9944.
### Somehow related changes
1. Literals should be in uppercase while placeholder in lowercase. Now all the GEO* command
will be documented with M|KM|FT|MI and can take both lowercase and uppercase
### Unrelated changes
1. Bugfix: no_madaory_keys was absent in COMMAND's reply
2. expose CMD_MODULE as "module" via COMMAND
3. have a dedicated uint64 for ACL categories (instead of having them in the same uint64 as command flags)
Co-authored-by: Itamar Haber <itamar@garantiadata.com>
2021-12-15 20:23:15 +01:00
if ( cmd - > acl_categories & cflag ) {
2022-01-20 13:05:27 -08:00
ACLChangeSelectorPerm ( selector , cmd , value ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
}
if ( cmd - > subcommands_dict ) {
2022-11-03 10:14:56 -07:00
ACLSetSelectorCommandBitsForCategory ( cmd - > subcommands_dict , selector , cflag , value ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
}
}
dictReleaseIterator ( di ) ;
}
2023-03-21 10:07:11 -07:00
/* This function is responsible for recomputing the command bits for all selectors of the existing users.
* It uses the ' command_rules ' , a string representation of the ordered categories and commands ,
* to recompute the command bits . */
2023-05-02 17:31:32 -07:00
void ACLRecomputeCommandBitsFromCommandRulesAllUsers ( void ) {
2023-03-21 10:07:11 -07:00
raxIterator ri ;
raxStart ( & ri , Users ) ;
raxSeek ( & ri , " ^ " , NULL , 0 ) ;
while ( raxNext ( & ri ) ) {
user * u = ri . data ;
listIter li ;
listNode * ln ;
listRewind ( u - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * selector = ( aclSelector * ) listNodeValue ( ln ) ;
int argc = 0 ;
sds * argv = sdssplitargs ( selector - > command_rules , & argc ) ;
serverAssert ( argv ! = NULL ) ;
/* Checking selector's permissions for all commands to start with a clean state. */
if ( ACLSelectorCanExecuteFutureCommands ( selector ) ) {
int res = ACLSetSelector ( selector , " +@all " , - 1 ) ;
serverAssert ( res = = C_OK ) ;
} else {
int res = ACLSetSelector ( selector , " -@all " , - 1 ) ;
serverAssert ( res = = C_OK ) ;
}
/* Apply all of the commands and categories to this selector. */
for ( int i = 0 ; i < argc ; i + + ) {
int res = ACLSetSelector ( selector , argv [ i ] , sdslen ( argv [ i ] ) ) ;
serverAssert ( res = = C_OK ) ;
}
sdsfreesplitres ( argv , argc ) ;
}
}
raxStop ( & ri ) ;
}
2022-11-03 10:14:56 -07:00
int ACLSetSelectorCategory ( aclSelector * selector , const char * category , int allow ) {
uint64_t cflag = ACLGetCommandCategoryFlagByName ( category + 1 ) ;
2019-01-24 18:11:09 +01:00
if ( ! cflag ) return C_ERR ;
2022-11-03 10:14:56 -07:00
ACLUpdateCommandRules ( selector , category , allow ) ;
/* Set the actual command bits on the selector. */
ACLSetSelectorCommandBitsForCategory ( server . orig_commands , selector , cflag , allow ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
return C_OK ;
}
2022-01-20 13:05:27 -08:00
void ACLCountCategoryBitsForCommands ( dict * commands , aclSelector * selector , unsigned long * on , unsigned long * off , uint64_t cflag ) {
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
dictIterator * di = dictGetIterator ( commands ) ;
2019-01-24 18:11:09 +01:00
dictEntry * de ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd = dictGetVal ( de ) ;
Auto-generate the command table from JSON files (#9656)
Delete the hardcoded command table and replace it with an auto-generated table, based
on a JSON file that describes the commands (each command must have a JSON file).
These JSON files are the SSOT of everything there is to know about Redis commands,
and it is reflected fully in COMMAND INFO.
These JSON files are used to generate commands.c (using a python script), which is then
committed to the repo and compiled.
The purpose is:
* Clients and proxies will be able to get much more info from redis, instead of relying on hard coded logic.
* drop the dependency between Redis-user and the commands.json in redis-doc.
* delete help.h and have redis-cli learn everything it needs to know just by issuing COMMAND (will be
done in a separate PR)
* redis.io should stop using commands.json and learn everything from Redis (ultimately one of the release
artifacts should be a large JSON, containing all the information about all of the commands, which will be
generated from COMMAND's reply)
* the byproduct of this is:
* module commands will be able to provide that info and possibly be more of a first-class citizens
* in theory, one may be able to generate a redis client library for a strictly typed language, by using this info.
### Interface changes
#### COMMAND INFO's reply change (and arg-less COMMAND)
Before this commit the reply at index 7 contained the key-specs list
and reply at index 8 contained the sub-commands list (Both unreleased).
Now, reply at index 7 is a map of:
- summary - short command description
- since - debut version
- group - command group
- complexity - complexity string
- doc-flags - flags used for documentation (e.g. "deprecated")
- deprecated-since - if deprecated, from which version?
- replaced-by - if deprecated, which command replaced it?
- history - a list of (version, what-changed) tuples
- hints - a list of strings, meant to provide hints for clients/proxies. see https://github.com/redis/redis/issues/9876
- arguments - an array of arguments. each element is a map, with the possibility of nesting (sub-arguments)
- key-specs - an array of keys specs (already in unstable, just changed location)
- subcommands - a list of sub-commands (already in unstable, just changed location)
- reply-schema - will be added in the future (see https://github.com/redis/redis/issues/9845)
more details on these can be found in https://github.com/redis/redis-doc/pull/1697
only the first three fields are mandatory
#### API changes (unreleased API obviously)
now they take RedisModuleCommand opaque pointer instead of looking up the command by name
- RM_CreateSubcommand
- RM_AddCommandKeySpec
- RM_SetCommandKeySpecBeginSearchIndex
- RM_SetCommandKeySpecBeginSearchKeyword
- RM_SetCommandKeySpecFindKeysRange
- RM_SetCommandKeySpecFindKeysKeynum
Currently, we did not add module API to provide additional information about their commands because
we couldn't agree on how the API should look like, see https://github.com/redis/redis/issues/9944.
### Somehow related changes
1. Literals should be in uppercase while placeholder in lowercase. Now all the GEO* command
will be documented with M|KM|FT|MI and can take both lowercase and uppercase
### Unrelated changes
1. Bugfix: no_madaory_keys was absent in COMMAND's reply
2. expose CMD_MODULE as "module" via COMMAND
3. have a dedicated uint64 for ACL categories (instead of having them in the same uint64 as command flags)
Co-authored-by: Itamar Haber <itamar@garantiadata.com>
2021-12-15 20:23:15 +01:00
if ( cmd - > acl_categories & cflag ) {
2022-01-20 13:05:27 -08:00
if ( ACLGetSelectorCommandBit ( selector , cmd - > id ) )
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
( * on ) + + ;
else
( * off ) + + ;
}
if ( cmd - > subcommands_dict ) {
2022-01-20 13:05:27 -08:00
ACLCountCategoryBitsForCommands ( cmd - > subcommands_dict , selector , on , off , cflag ) ;
2019-01-30 08:09:05 +01:00
}
2019-01-24 18:11:09 +01:00
}
dictReleaseIterator ( di ) ;
}
2019-01-29 17:25:02 +01:00
/* Return the number of commands allowed (on) and denied (off) for the user 'u'
* in the subset of commands flagged with the specified category name .
2019-06-07 13:20:22 -07:00
* If the category name is not valid , C_ERR is returned , otherwise C_OK is
2019-01-29 17:25:02 +01:00
* returned and on and off are populated by reference . */
2022-01-20 13:05:27 -08:00
int ACLCountCategoryBitsForSelector ( aclSelector * selector , unsigned long * on , unsigned long * off ,
2019-01-29 17:25:02 +01:00
const char * category )
{
uint64_t cflag = ACLGetCommandCategoryFlagByName ( category ) ;
if ( ! cflag ) return C_ERR ;
* on = * off = 0 ;
2022-01-20 13:05:27 -08:00
ACLCountCategoryBitsForCommands ( server . orig_commands , selector , on , off , cflag ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
return C_OK ;
}
2022-01-20 13:05:27 -08:00
/* This function returns an SDS string representing the specified selector ACL
2019-01-29 17:25:02 +01:00
* rules related to command execution , in the same format you could set them
* back using ACL SETUSER . The function will return just the set of rules needed
* to recreate the user commands bitmap , without including other user flags such
* as on / off , passwords and so forth . The returned string always starts with
* the + @ all or - @ all rule , depending on the user bitmap , and is followed , if
* needed , by the other rules needed to narrow or extend what the user can do . */
2022-01-20 13:05:27 -08:00
sds ACLDescribeSelectorCommandRules ( aclSelector * selector ) {
2019-01-29 17:25:02 +01:00
sds rules = sdsempty ( ) ;
2022-11-03 10:14:56 -07:00
/* We use this fake selector as a "sanity" check to make sure the rules
* we generate have the same bitmap as those on the current selector . */
aclSelector * fake_selector = ACLCreateSelector ( 0 ) ;
/* Here we want to understand if we should start with +@all or -@all.
* Note that when starting with + @ all and subtracting , the user
2019-01-29 17:25:02 +01:00
* will be able to execute future commands , while - @ all and adding will just
* allow the user the run the selected commands and / or categories .
* How do we test for that ? We use the trick of a reserved command ID bit
* that is set only by + @ all ( and its alias " allcommands " ) . */
2022-01-20 13:05:27 -08:00
if ( ACLSelectorCanExecuteFutureCommands ( selector ) ) {
2019-01-29 17:25:02 +01:00
rules = sdscat ( rules , " +@all " ) ;
2022-01-20 13:05:27 -08:00
ACLSetSelector ( fake_selector , " +@all " , - 1 ) ;
2019-01-29 17:25:02 +01:00
} else {
rules = sdscat ( rules , " -@all " ) ;
2022-01-20 13:05:27 -08:00
ACLSetSelector ( fake_selector , " -@all " , - 1 ) ;
2019-01-29 17:25:02 +01:00
}
2022-11-03 10:14:56 -07:00
/* Apply all of the commands and categories to the fake selector. */
int argc = 0 ;
sds * argv = sdssplitargs ( selector - > command_rules , & argc ) ;
serverAssert ( argv ! = NULL ) ;
2019-01-29 17:25:02 +01:00
2022-11-03 10:14:56 -07:00
for ( int i = 0 ; i < argc ; i + + ) {
int res = ACLSetSelector ( fake_selector , argv [ i ] , - 1 ) ;
serverAssert ( res = = C_OK ) ;
2020-10-27 12:47:02 -07:00
}
2022-11-03 10:14:56 -07:00
if ( sdslen ( selector - > command_rules ) ) {
rules = sdscatfmt ( rules , " %S " , selector - > command_rules ) ;
}
sdsfreesplitres ( argv , argc ) ;
2019-01-29 17:25:02 +01:00
/* Trim the final useless space. */
2019-01-29 18:41:11 +01:00
sdsrange ( rules , 0 , - 2 ) ;
2019-01-29 17:25:02 +01:00
/* This is technically not needed, but we want to verify that now the
* predicted bitmap is exactly the same as the user bitmap , and abort
* otherwise , because aborting is better than a security risk in this
* code path . */
2022-01-20 13:05:27 -08:00
if ( memcmp ( fake_selector - > allowed_commands ,
selector - > allowed_commands ,
sizeof ( selector - > allowed_commands ) ) ! = 0 )
2019-01-29 18:54:21 +01:00
{
serverLog ( LL_WARNING ,
" CRITICAL ERROR: User ACLs don't match final bitmap: '%s' " ,
rules ) ;
2022-01-20 13:05:27 -08:00
serverPanic ( " No bitmap match in ACLDescribeSelectorCommandRules() " ) ;
2019-01-29 18:54:21 +01:00
}
2022-11-03 10:14:56 -07:00
ACLFreeSelector ( fake_selector ) ;
2019-01-29 17:25:02 +01:00
return rules ;
}
2022-01-20 13:05:27 -08:00
sds ACLDescribeSelector ( aclSelector * selector ) {
listIter li ;
listNode * ln ;
sds res = sdsempty ( ) ;
/* Key patterns. */
if ( selector - > flags & SELECTOR_FLAG_ALLKEYS ) {
res = sdscatlen ( res , " ~* " , 3 ) ;
} else {
listRewind ( selector - > patterns , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
keyPattern * thispat = ( keyPattern * ) listNodeValue ( ln ) ;
res = sdsCatPatternString ( res , thispat ) ;
res = sdscatlen ( res , " " , 1 ) ;
}
}
/* Pub/sub channel patterns. */
if ( selector - > flags & SELECTOR_FLAG_ALLCHANNELS ) {
res = sdscatlen ( res , " &* " , 3 ) ;
} else {
res = sdscatlen ( res , " resetchannels " , 14 ) ;
listRewind ( selector - > channels , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
sds thispat = listNodeValue ( ln ) ;
res = sdscatlen ( res , " & " , 1 ) ;
res = sdscatsds ( res , thispat ) ;
res = sdscatlen ( res , " " , 1 ) ;
}
}
/* Command rules. */
sds rules = ACLDescribeSelectorCommandRules ( selector ) ;
res = sdscatsds ( res , rules ) ;
sdsfree ( rules ) ;
return res ;
}
/* This is similar to ACLDescribeSelectorCommandRules(), however instead of
2019-01-31 16:49:22 +01:00
* describing just the user command rules , everything is described : user
* flags , keys , passwords and finally the command rules obtained via
2022-01-20 13:05:27 -08:00
* the ACLDescribeSelectorCommandRules ( ) function . This is the function we call
2019-01-31 16:49:22 +01:00
* when we want to rewrite the configuration files describing ACLs and
* in order to show users with ACL LIST . */
2022-09-22 16:29:00 +03:00
robj * ACLDescribeUser ( user * u ) {
if ( u - > acl_string ) {
incrRefCount ( u - > acl_string ) ;
return u - > acl_string ;
}
2019-01-31 16:49:22 +01:00
sds res = sdsempty ( ) ;
/* Flags. */
for ( int j = 0 ; ACLUserFlags [ j ] . flag ; j + + ) {
if ( u - > flags & ACLUserFlags [ j ] . flag ) {
res = sdscat ( res , ACLUserFlags [ j ] . name ) ;
res = sdscatlen ( res , " " , 1 ) ;
}
}
/* Passwords. */
listIter li ;
listNode * ln ;
listRewind ( u - > passwords , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
sds thispass = listNodeValue ( ln ) ;
2019-09-17 03:32:35 -07:00
res = sdscatlen ( res , " # " , 1 ) ;
2019-01-31 16:49:22 +01:00
res = sdscatsds ( res , thispass ) ;
res = sdscatlen ( res , " " , 1 ) ;
}
2022-01-20 13:05:27 -08:00
/* Selectors (Commands and keys) */
listRewind ( u - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * selector = ( aclSelector * ) listNodeValue ( ln ) ;
sds default_perm = ACLDescribeSelector ( selector ) ;
if ( selector - > flags & SELECTOR_FLAG_ROOT ) {
res = sdscatfmt ( res , " %s " , default_perm ) ;
} else {
res = sdscatfmt ( res , " (%s) " , default_perm ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-01-20 13:05:27 -08:00
sdsfree ( default_perm ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-09-22 16:29:00 +03:00
u - > acl_string = createObject ( OBJ_STRING , res ) ;
/* because we are returning it, have to increase count */
incrRefCount ( u - > acl_string ) ;
return u - > acl_string ;
2019-01-31 16:49:22 +01:00
}
2019-01-18 18:16:03 +01:00
/* Get a command from the original command table, that is not affected
* by the command renaming operations : we base all the ACL work from that
* table , so that ACLs are valid regardless of command renaming . */
2024-04-03 23:54:33 +07:00
struct serverCommand * ACLLookupCommand ( const char * name ) {
struct serverCommand * cmd ;
2019-01-18 18:16:03 +01:00
sds sdsname = sdsnew ( name ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
cmd = lookupCommandBySdsLogic ( server . orig_commands , sdsname ) ;
2019-01-18 18:16:03 +01:00
sdsfree ( sdsname ) ;
return cmd ;
}
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
/* Flush the array of allowed first-args for the specified user
2019-01-21 17:08:54 +01:00
* and command ID . */
2022-01-20 13:05:27 -08:00
void ACLResetFirstArgsForCommand ( aclSelector * selector , unsigned long id ) {
if ( selector - > allowed_firstargs & & selector - > allowed_firstargs [ id ] ) {
for ( int i = 0 ; selector - > allowed_firstargs [ id ] [ i ] ; i + + )
sdsfree ( selector - > allowed_firstargs [ id ] [ i ] ) ;
zfree ( selector - > allowed_firstargs [ id ] ) ;
selector - > allowed_firstargs [ id ] = NULL ;
2019-01-21 17:08:54 +01:00
}
}
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
/* Flush the entire table of first-args. This is useful on +@all, -@all
2019-01-28 18:27:41 +01:00
* or similar to return back to the minimal memory usage ( and checks to do )
* for the user . */
2022-01-20 13:05:27 -08:00
void ACLResetFirstArgs ( aclSelector * selector ) {
if ( selector - > allowed_firstargs = = NULL ) return ;
2019-01-30 11:50:30 +01:00
for ( int j = 0 ; j < USER_COMMAND_BITS_COUNT ; j + + ) {
2022-01-20 13:05:27 -08:00
if ( selector - > allowed_firstargs [ j ] ) {
for ( int i = 0 ; selector - > allowed_firstargs [ j ] [ i ] ; i + + )
sdsfree ( selector - > allowed_firstargs [ j ] [ i ] ) ;
zfree ( selector - > allowed_firstargs [ j ] ) ;
2019-01-30 11:50:30 +01:00
}
}
2022-01-20 13:05:27 -08:00
zfree ( selector - > allowed_firstargs ) ;
selector - > allowed_firstargs = NULL ;
2019-01-28 18:27:41 +01:00
}
2023-09-08 21:10:17 +08:00
/* Add a first-arg to the list of subcommands for the user 'u' and
2019-01-28 18:40:54 +01:00
* the command id specified . */
2022-01-20 13:05:27 -08:00
void ACLAddAllowedFirstArg ( aclSelector * selector , unsigned long id , const char * sub ) {
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
/* If this is the first first-arg to be configured for
* this user , we have to allocate the first - args array . */
2022-01-20 13:05:27 -08:00
if ( selector - > allowed_firstargs = = NULL ) {
selector - > allowed_firstargs = zcalloc ( USER_COMMAND_BITS_COUNT * sizeof ( sds * ) ) ;
2019-01-28 18:40:54 +01:00
}
/* We also need to enlarge the allocation pointing to the
* null terminated SDS array , to make space for this one .
* To start check the current size , and while we are here
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
* make sure the first - arg is not already specified inside . */
2019-01-28 18:40:54 +01:00
long items = 0 ;
2022-01-20 13:05:27 -08:00
if ( selector - > allowed_firstargs [ id ] ) {
while ( selector - > allowed_firstargs [ id ] [ items ] ) {
2019-01-28 18:40:54 +01:00
/* If it's already here do not add it again. */
2022-01-20 13:05:27 -08:00
if ( ! strcasecmp ( selector - > allowed_firstargs [ id ] [ items ] , sub ) )
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
return ;
2019-01-28 18:40:54 +01:00
items + + ;
}
}
/* Now we can make space for the new item (and the null term). */
items + = 2 ;
2022-01-20 13:05:27 -08:00
selector - > allowed_firstargs [ id ] = zrealloc ( selector - > allowed_firstargs [ id ] , sizeof ( sds ) * items ) ;
selector - > allowed_firstargs [ id ] [ items - 2 ] = sdsnew ( sub ) ;
selector - > allowed_firstargs [ id ] [ items - 1 ] = NULL ;
2019-01-28 18:40:54 +01:00
}
2022-01-20 13:05:27 -08:00
/* Create an ACL selector from the given ACL operations, which should be
* a list of space separate ACL operations that starts and ends
* with parentheses .
*
* If any of the operations are invalid , NULL will be returned instead
* and errno will be set corresponding to the interior error . */
aclSelector * aclCreateSelectorFromOpSet ( const char * opset , size_t opsetlen ) {
serverAssert ( opset [ 0 ] = = ' ( ' & & opset [ opsetlen - 1 ] = = ' ) ' ) ;
aclSelector * s = ACLCreateSelector ( 0 ) ;
int argc = 0 ;
sds trimmed = sdsnewlen ( opset + 1 , opsetlen - 2 ) ;
sds * argv = sdssplitargs ( trimmed , & argc ) ;
for ( int i = 0 ; i < argc ; i + + ) {
if ( ACLSetSelector ( s , argv [ i ] , sdslen ( argv [ i ] ) ) = = C_ERR ) {
ACLFreeSelector ( s ) ;
s = NULL ;
goto cleanup ;
}
}
cleanup :
sdsfreesplitres ( argv , argc ) ;
sdsfree ( trimmed ) ;
return s ;
}
/* Set a selector's properties with the provided 'op'.
2019-01-11 11:25:55 +01:00
*
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
* + < command > Allow the execution of that command .
* May be used with ` | ` for allowing subcommands ( e . g " +config|get " )
* - < command > Disallow the execution of that command .
* May be used with ` | ` for blocking subcommands ( e . g " -config|set " )
2019-01-11 11:25:55 +01:00
* + @ < category > Allow the execution of all the commands in such category
2019-01-23 12:15:10 +01:00
* with valid categories are like @ admin , @ set , @ sortedset , . . .
* and so forth , see the full list in the server . c file where
2024-04-09 01:24:03 -07:00
* the command table is described and defined .
2019-01-23 11:05:54 +01:00
* The special category @ all means all the commands , but currently
* present in the server , and that will be loaded in the future
* via modules .
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
* + < command > | first - arg Allow a specific first argument of an otherwise
* disabled command . Note that this form is not
* allowed as negative like - SELECT | 1 , but
* only additive starting with " + " .
2019-01-23 11:05:54 +01:00
* allcommands Alias for + @ all . Note that it implies the ability to execute
* all the future commands loaded via the modules system .
* nocommands Alias for - @ all .
2019-01-11 13:03:50 +01:00
* ~ < pattern > Add a pattern of keys that can be mentioned as part of
2019-01-11 11:25:55 +01:00
* commands . For instance ~ * allows all the keys . The pattern
* is a glob - style pattern like the one of KEYS .
2019-01-11 13:03:50 +01:00
* It is possible to specify multiple patterns .
2022-01-23 16:05:06 +08:00
* % R ~ < pattern > Add key read pattern that specifies which keys can be read
2022-01-20 13:05:27 -08:00
* from .
* % W ~ < pattern > Add key write pattern that specifies which keys can be
2022-01-23 16:05:06 +08:00
* written to .
2019-01-21 18:23:28 +01:00
* allkeys Alias for ~ *
* resetkeys Flush the list of allowed keys patterns .
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
* & < pattern > Add a pattern of channels that can be mentioned as part of
* Pub / Sub commands . For instance & * allows all the channels . The
* pattern is a glob - style pattern like the one of PSUBSCRIBE .
* It is possible to specify multiple patterns .
* allchannels Alias for & *
2022-01-23 16:05:06 +08:00
* resetchannels Flush the list of allowed channel patterns .
2019-01-30 08:25:08 +01:00
*/
2022-01-20 13:05:27 -08:00
int ACLSetSelector ( aclSelector * selector , const char * op , size_t oplen ) {
if ( ! strcasecmp ( op , " allkeys " ) | |
2019-01-11 13:03:50 +01:00
! strcasecmp ( op , " ~* " ) )
{
2022-01-20 13:05:27 -08:00
selector - > flags | = SELECTOR_FLAG_ALLKEYS ;
listEmpty ( selector - > patterns ) ;
2019-01-21 18:18:39 +01:00
} else if ( ! strcasecmp ( op , " resetkeys " ) ) {
2022-01-20 13:05:27 -08:00
selector - > flags & = ~ SELECTOR_FLAG_ALLKEYS ;
listEmpty ( selector - > patterns ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
} else if ( ! strcasecmp ( op , " allchannels " ) | |
! strcasecmp ( op , " &* " ) )
{
2022-01-20 13:05:27 -08:00
selector - > flags | = SELECTOR_FLAG_ALLCHANNELS ;
listEmpty ( selector - > channels ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
} else if ( ! strcasecmp ( op , " resetchannels " ) ) {
2022-01-20 13:05:27 -08:00
selector - > flags & = ~ SELECTOR_FLAG_ALLCHANNELS ;
listEmpty ( selector - > channels ) ;
2019-01-14 13:18:12 +01:00
} else if ( ! strcasecmp ( op , " allcommands " ) | |
! strcasecmp ( op , " +@all " ) )
{
2022-01-20 13:05:27 -08:00
memset ( selector - > allowed_commands , 255 , sizeof ( selector - > allowed_commands ) ) ;
selector - > flags | = SELECTOR_FLAG_ALLCOMMANDS ;
2022-11-03 10:14:56 -07:00
sdsclear ( selector - > command_rules ) ;
2022-01-20 13:05:27 -08:00
ACLResetFirstArgs ( selector ) ;
2019-01-21 18:23:28 +01:00
} else if ( ! strcasecmp ( op , " nocommands " ) | |
! strcasecmp ( op , " -@all " ) )
{
2022-01-20 13:05:27 -08:00
memset ( selector - > allowed_commands , 0 , sizeof ( selector - > allowed_commands ) ) ;
selector - > flags & = ~ SELECTOR_FLAG_ALLCOMMANDS ;
2022-11-03 10:14:56 -07:00
sdsclear ( selector - > command_rules ) ;
2022-01-20 13:05:27 -08:00
ACLResetFirstArgs ( selector ) ;
} else if ( op [ 0 ] = = ' ~ ' | | op [ 0 ] = = ' % ' ) {
if ( selector - > flags & SELECTOR_FLAG_ALLKEYS ) {
errno = EEXIST ;
return C_ERR ;
2019-09-17 03:32:35 -07:00
}
2022-01-20 13:05:27 -08:00
int flags = 0 ;
size_t offset = 1 ;
if ( op [ 0 ] = = ' % ' ) {
for ( ; offset < oplen ; offset + + ) {
if ( toupper ( op [ offset ] ) = = ' R ' & & ! ( flags & ACL_READ_PERMISSION ) ) {
flags | = ACL_READ_PERMISSION ;
} else if ( toupper ( op [ offset ] ) = = ' W ' & & ! ( flags & ACL_WRITE_PERMISSION ) ) {
flags | = ACL_WRITE_PERMISSION ;
} else if ( op [ offset ] = = ' ~ ' ) {
offset + + ;
break ;
} else {
errno = EINVAL ;
return C_ERR ;
}
2019-09-30 18:22:55 +02:00
}
2019-02-11 17:00:51 +01:00
} else {
2022-01-20 13:05:27 -08:00
flags = ACL_ALL_PERMISSION ;
2019-02-11 17:00:51 +01:00
}
2022-01-20 13:05:27 -08:00
if ( ACLStringHasSpaces ( op + offset , oplen - offset ) ) {
2020-04-15 16:39:42 +02:00
errno = EINVAL ;
return C_ERR ;
2020-04-15 16:12:06 +02:00
}
2022-01-20 13:05:27 -08:00
keyPattern * newpat = ACLKeyPatternCreate ( sdsnewlen ( op + offset , oplen - offset ) , flags ) ;
listNode * ln = listSearchKey ( selector - > patterns , newpat ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
/* Avoid re-adding the same key pattern multiple times. */
2022-01-20 13:05:27 -08:00
if ( ln = = NULL ) {
listAddNodeTail ( selector - > patterns , newpat ) ;
} else {
( ( keyPattern * ) listNodeValue ( ln ) ) - > flags | = flags ;
ACLKeyPatternFree ( newpat ) ;
}
selector - > flags & = ~ SELECTOR_FLAG_ALLKEYS ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
} else if ( op [ 0 ] = = ' & ' ) {
2022-01-20 13:05:27 -08:00
if ( selector - > flags & SELECTOR_FLAG_ALLCHANNELS ) {
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
errno = EISDIR ;
return C_ERR ;
}
if ( ACLStringHasSpaces ( op + 1 , oplen - 1 ) ) {
errno = EINVAL ;
return C_ERR ;
}
sds newpat = sdsnewlen ( op + 1 , oplen - 1 ) ;
2022-01-20 13:05:27 -08:00
listNode * ln = listSearchKey ( selector - > channels , newpat ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
/* Avoid re-adding the same channel pattern multiple times. */
if ( ln = = NULL )
2022-01-20 13:05:27 -08:00
listAddNodeTail ( selector - > channels , newpat ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
else
sdsfree ( newpat ) ;
2022-01-20 13:05:27 -08:00
selector - > flags & = ~ SELECTOR_FLAG_ALLCHANNELS ;
2019-01-18 18:16:03 +01:00
} else if ( op [ 0 ] = = ' + ' & & op [ 1 ] ! = ' @ ' ) {
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
if ( strrchr ( op , ' | ' ) = = NULL ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd = ACLLookupCommand ( op + 1 ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
if ( cmd = = NULL ) {
2019-01-21 17:08:54 +01:00
errno = ENOENT ;
return C_ERR ;
}
2022-01-20 13:05:27 -08:00
ACLChangeSelectorPerm ( selector , cmd , 1 ) ;
2022-11-03 10:14:56 -07:00
ACLUpdateCommandRules ( selector , cmd - > fullname , 1 ) ;
2019-01-21 17:08:54 +01:00
} else {
/* Split the command and subcommand parts. */
char * copy = zstrdup ( op + 1 ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
char * sub = strrchr ( copy , ' | ' ) ;
2019-01-21 17:08:54 +01:00
sub [ 0 ] = ' \0 ' ;
sub + + ;
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd = ACLLookupCommand ( copy ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
2019-01-21 17:08:54 +01:00
/* Check if the command exists. We can't check the
2022-01-22 13:09:40 +01:00
* first - arg to see if it is valid . */
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
if ( cmd = = NULL ) {
2019-01-30 08:20:31 +01:00
zfree ( copy ) ;
2019-01-21 17:08:54 +01:00
errno = ENOENT ;
return C_ERR ;
}
2022-01-22 13:09:40 +01:00
/* We do not support allowing first-arg of a subcommand */
if ( cmd - > parent ) {
zfree ( copy ) ;
errno = ECHILD ;
return C_ERR ;
}
2019-01-21 17:08:54 +01:00
/* The subcommand cannot be empty, so things like DEBUG|
* are syntax errors of course . */
if ( strlen ( sub ) = = 0 ) {
zfree ( copy ) ;
errno = EINVAL ;
return C_ERR ;
}
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
if ( cmd - > subcommands_dict ) {
/* If user is trying to allow a valid subcommand we can just add its unique ID */
2022-01-22 13:09:40 +01:00
cmd = ACLLookupCommand ( op + 1 ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
if ( cmd = = NULL ) {
zfree ( copy ) ;
errno = ENOENT ;
return C_ERR ;
}
2022-01-20 13:05:27 -08:00
ACLChangeSelectorPerm ( selector , cmd , 1 ) ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
} else {
/* If user is trying to use the ACL mech to block SELECT except SELECT 0 or
* block DEBUG except DEBUG OBJECT ( DEBUG subcommands are not considered
* subcommands for now ) we use the allowed_firstargs mechanism . */
2022-01-22 13:09:40 +01:00
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
/* Add the first-arg to the list of valid ones. */
2022-01-22 13:09:40 +01:00
serverLog ( LL_WARNING , " Deprecation warning: Allowing a first arg of an otherwise "
" blocked command is a misuse of ACL and may get disabled "
" in the future (offender: +%s) " , op + 1 ) ;
2022-01-20 13:05:27 -08:00
ACLAddAllowedFirstArg ( selector , cmd - > id , sub ) ;
}
2022-11-03 10:14:56 -07:00
ACLUpdateCommandRules ( selector , op + 1 , 1 ) ;
2022-01-20 13:05:27 -08:00
zfree ( copy ) ;
}
} else if ( op [ 0 ] = = ' - ' & & op [ 1 ] ! = ' @ ' ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd = ACLLookupCommand ( op + 1 ) ;
2022-01-20 13:05:27 -08:00
if ( cmd = = NULL ) {
errno = ENOENT ;
return C_ERR ;
}
ACLChangeSelectorPerm ( selector , cmd , 0 ) ;
2022-11-03 10:14:56 -07:00
ACLUpdateCommandRules ( selector , cmd - > fullname , 0 ) ;
2022-01-20 13:05:27 -08:00
} else if ( ( op [ 0 ] = = ' + ' | | op [ 0 ] = = ' - ' ) & & op [ 1 ] = = ' @ ' ) {
int bitval = op [ 0 ] = = ' + ' ? 1 : 0 ;
2022-11-03 10:14:56 -07:00
if ( ACLSetSelectorCategory ( selector , op + 1 , bitval ) = = C_ERR ) {
2022-01-20 13:05:27 -08:00
errno = ENOENT ;
return C_ERR ;
}
} else {
errno = EINVAL ;
return C_ERR ;
}
return C_OK ;
}
/* Set user properties according to the string "op". The following
* is a description of what different strings will do :
*
* on Enable the user : it is possible to authenticate as this user .
* off Disable the user : it ' s no longer possible to authenticate
* with this user , however the already authenticated connections
* will still work .
2022-09-22 14:13:39 +08:00
* skip - sanitize - payload RESTORE dump - payload sanitization is skipped .
* sanitize - payload RESTORE dump - payload is sanitized ( default ) .
2022-01-20 13:05:27 -08:00
* > < password > Add this password to the list of valid password for the user .
* For example > mypass will add " mypass " to the list .
* This directive clears the " nopass " flag ( see later ) .
* # < hash > Add this password hash to the list of valid hashes for
* the user . This is useful if you have previously computed
* the hash , and don ' t want to store it in plaintext .
* This directive clears the " nopass " flag ( see later ) .
* < < password > Remove this password from the list of valid passwords .
* ! < hash > Remove this hashed password from the list of valid passwords .
* This is useful when you want to remove a password just by
* hash without knowing its plaintext version at all .
* nopass All the set passwords of the user are removed , and the user
* is flagged as requiring no password : it means that every
* password will work against this user . If this directive is
* used for the default user , every new connection will be
* immediately authenticated with the default user without
* any explicit AUTH command required . Note that the " resetpass "
* directive will clear this condition .
* resetpass Flush the list of allowed passwords . Moreover removes the
* " nopass " status . After " resetpass " the user has no associated
* passwords and there is no way to authenticate without adding
* some password ( or setting it as " nopass " later ) .
2022-09-22 14:13:39 +08:00
* reset Performs the following actions : resetpass , resetkeys , resetchannels ,
* allchannels ( if acl - pubsub - default is set ) , off , clearselectors , - @ all .
* The user returns to the same state it has immediately after its creation .
2022-01-20 13:05:27 -08:00
* ( < options > ) Create a new selector with the options specified within the
* parentheses and attach it to the user . Each option should be
* space separated . The first character must be ( and the last
* character must be ) .
* clearselectors Remove all of the currently attached selectors .
* Note this does not change the " root " user permissions ,
* which are the permissions directly applied onto the
* user ( outside the parentheses ) .
*
* Selector options can also be specified by this function , in which case
* they update the root selector for the user .
*
* The ' op ' string must be null terminated . The ' oplen ' argument should
* specify the length of the ' op ' string in case the caller requires to pass
* binary data ( for instance the > password form may use a binary password ) .
* Otherwise the field can be set to - 1 and the function will use strlen ( )
* to determine the length .
*
* The function returns C_OK if the action to perform was understood because
* the ' op ' string made sense . Otherwise C_ERR is returned if the operation
* is unknown or has some syntax error .
*
* When an error is returned , errno is set to the following values :
*
* EINVAL : The specified opcode is not understood or the key / channel pattern is
* invalid ( contains non allowed characters ) .
* ENOENT : The command name or command category provided with + or - is not
* known .
* EEXIST : You are adding a key pattern after " * " was already added . This is
* almost surely an error on the user side .
* EISDIR : You are adding a channel pattern after " * " was already added . This is
* almost surely an error on the user side .
* ENODEV : The password you are trying to remove from the user does not exist .
* EBADMSG : The hash you are trying to add is not a valid hash .
2022-01-22 13:09:40 +01:00
* ECHILD : Attempt to allow a specific first argument of a subcommand
2022-01-20 13:05:27 -08:00
*/
int ACLSetUser ( user * u , const char * op , ssize_t oplen ) {
2022-09-22 16:29:00 +03:00
/* as we are changing the ACL, the old generated string is now invalid */
if ( u - > acl_string ) {
decrRefCount ( u - > acl_string ) ;
u - > acl_string = NULL ;
}
2022-01-20 13:05:27 -08:00
if ( oplen = = - 1 ) oplen = strlen ( op ) ;
if ( oplen = = 0 ) return C_OK ; /* Empty string is a no-operation. */
if ( ! strcasecmp ( op , " on " ) ) {
u - > flags | = USER_FLAG_ENABLED ;
u - > flags & = ~ USER_FLAG_DISABLED ;
} else if ( ! strcasecmp ( op , " off " ) ) {
u - > flags | = USER_FLAG_DISABLED ;
u - > flags & = ~ USER_FLAG_ENABLED ;
} else if ( ! strcasecmp ( op , " skip-sanitize-payload " ) ) {
u - > flags | = USER_FLAG_SANITIZE_PAYLOAD_SKIP ;
u - > flags & = ~ USER_FLAG_SANITIZE_PAYLOAD ;
} else if ( ! strcasecmp ( op , " sanitize-payload " ) ) {
u - > flags & = ~ USER_FLAG_SANITIZE_PAYLOAD_SKIP ;
u - > flags | = USER_FLAG_SANITIZE_PAYLOAD ;
} else if ( ! strcasecmp ( op , " nopass " ) ) {
u - > flags | = USER_FLAG_NOPASS ;
listEmpty ( u - > passwords ) ;
} else if ( ! strcasecmp ( op , " resetpass " ) ) {
u - > flags & = ~ USER_FLAG_NOPASS ;
listEmpty ( u - > passwords ) ;
} else if ( op [ 0 ] = = ' > ' | | op [ 0 ] = = ' # ' ) {
sds newpass ;
if ( op [ 0 ] = = ' > ' ) {
newpass = ACLHashPassword ( ( unsigned char * ) op + 1 , oplen - 1 ) ;
} else {
if ( ACLCheckPasswordHash ( ( unsigned char * ) op + 1 , oplen - 1 ) = = C_ERR ) {
errno = EBADMSG ;
return C_ERR ;
2019-01-30 08:25:08 +01:00
}
2022-01-20 13:05:27 -08:00
newpass = sdsnewlen ( op + 1 , oplen - 1 ) ;
}
2019-01-30 08:25:08 +01:00
2022-01-20 13:05:27 -08:00
listNode * ln = listSearchKey ( u - > passwords , newpass ) ;
/* Avoid re-adding the same password multiple times. */
if ( ln = = NULL )
listAddNodeTail ( u - > passwords , newpass ) ;
else
sdsfree ( newpass ) ;
u - > flags & = ~ USER_FLAG_NOPASS ;
} else if ( op [ 0 ] = = ' < ' | | op [ 0 ] = = ' ! ' ) {
sds delpass ;
if ( op [ 0 ] = = ' < ' ) {
delpass = ACLHashPassword ( ( unsigned char * ) op + 1 , oplen - 1 ) ;
} else {
if ( ACLCheckPasswordHash ( ( unsigned char * ) op + 1 , oplen - 1 ) = = C_ERR ) {
errno = EBADMSG ;
return C_ERR ;
}
delpass = sdsnewlen ( op + 1 , oplen - 1 ) ;
2019-01-18 18:16:03 +01:00
}
2022-01-20 13:05:27 -08:00
listNode * ln = listSearchKey ( u - > passwords , delpass ) ;
sdsfree ( delpass ) ;
if ( ln ) {
listDelNode ( u - > passwords , ln ) ;
} else {
errno = ENODEV ;
2019-01-18 18:16:03 +01:00
return C_ERR ;
}
2022-01-20 13:05:27 -08:00
} else if ( op [ 0 ] = = ' ( ' & & op [ oplen - 1 ] = = ' ) ' ) {
aclSelector * selector = aclCreateSelectorFromOpSet ( op , oplen ) ;
if ( ! selector ) {
/* No errorno set, propagate it from interior error. */
2019-01-24 18:15:46 +01:00
return C_ERR ;
}
2022-01-20 13:05:27 -08:00
listAddNodeTail ( u - > selectors , selector ) ;
return C_OK ;
} else if ( ! strcasecmp ( op , " clearselectors " ) ) {
listIter li ;
listNode * ln ;
listRewind ( u - > selectors , & li ) ;
/* There has to be a root selector */
serverAssert ( listNext ( & li ) ) ;
while ( ( ln = listNext ( & li ) ) ) {
listDelNode ( u - > selectors , ln ) ;
}
return C_OK ;
2019-01-21 18:21:02 +01:00
} else if ( ! strcasecmp ( op , " reset " ) ) {
serverAssert ( ACLSetUser ( u , " resetpass " , - 1 ) = = C_OK ) ;
serverAssert ( ACLSetUser ( u , " resetkeys " , - 1 ) = = C_OK ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
serverAssert ( ACLSetUser ( u , " resetchannels " , - 1 ) = = C_OK ) ;
2022-01-20 13:05:27 -08:00
if ( server . acl_pubsub_default & SELECTOR_FLAG_ALLCHANNELS )
Fix "default" and overwritten / reset users will not have pubsub channels permissions by default. (#8723)
Background:
Redis 6.2 added ACL control for pubsub channels (#7993), which were supposed
to be permissive by default to retain compatibility with redis 6.0 ACL.
But due to a bug, only newly created users got this `acl-pubsub-default` applied,
while overwritten (updated) users got reset to `resetchannels` (denied).
Since the "default" user exists before loading the config file,
any ACL change to it, results in an update / overwrite.
So when a "default" user is loaded from config file or include ACL
file with no channels related rules, the user will not have any
permissions to any channels. But other users will have default
permissions to any channels.
When upgraded from 6.0 with config rewrite, this will lead to
"default" user channels permissions lost.
When users are loaded from include file, then call "acl load", users
will also lost channels permissions.
Similarly, the `reset` ACL rule, would have reset the user to be denied
access to any channels, ignoring `acl-pubsub-default` and breaking
compatibility with redis 6.0.
The implication of this fix is that it regains compatibility with redis 6.0,
but breaks compatibility with redis 6.2.0 and 2.0.1. e.g. after the upgrade,
the default user will regain access to pubsub channels.
Other changes:
Additionally this commit rename server.acl_pubusub_default to
server.acl_pubsub_default and fix typo in acl tests.
2021-04-06 04:13:20 +08:00
serverAssert ( ACLSetUser ( u , " allchannels " , - 1 ) = = C_OK ) ;
2019-01-21 18:21:02 +01:00
serverAssert ( ACLSetUser ( u , " off " , - 1 ) = = C_OK ) ;
Sanitize dump payload: ziplist, listpack, zipmap, intset, stream
When loading an encoded payload we will at least do a shallow validation to
check that the size that's encoded in the payload matches the size of the
allocation.
This let's us later use this encoded size to make sure the various offsets
inside encoded payload don't reach outside the allocation, if they do, we'll
assert/panic, but at least we won't segfault or smear memory.
We can also do 'deep' validation which runs on all the records of the encoded
payload and validates that they don't contain invalid offsets. This lets us
detect corruptions early and reject a RESTORE command rather than accepting
it and asserting (crashing) later when accessing that payload via some command.
configuration:
- adding ACL flag skip-sanitize-payload
- adding config sanitize-dump-payload [yes/no/clients]
For now, we don't have a good way to ensure MIGRATE in cluster resharding isn't
being slowed down by these sanitation, so i'm setting the default value to `no`,
but later on it should be set to `clients` by default.
changes:
- changing rdbReportError not to `exit` in RESTORE command
- adding a new stat to be able to later check if cluster MIGRATE isn't being
slowed down by sanitation.
2020-08-13 16:41:05 +03:00
serverAssert ( ACLSetUser ( u , " sanitize-payload " , - 1 ) = = C_OK ) ;
2022-01-20 13:05:27 -08:00
serverAssert ( ACLSetUser ( u , " clearselectors " , - 1 ) = = C_OK ) ;
2019-01-21 18:21:02 +01:00
serverAssert ( ACLSetUser ( u , " -@all " , - 1 ) = = C_OK ) ;
2019-01-11 13:03:50 +01:00
} else {
2022-01-20 13:05:27 -08:00
aclSelector * selector = ACLUserGetRootSelector ( u ) ;
if ( ACLSetSelector ( selector , op , oplen ) = = C_ERR ) {
return C_ERR ;
}
2019-01-11 13:03:50 +01:00
}
return C_OK ;
2019-01-11 11:25:55 +01:00
}
2019-01-30 16:02:25 +01:00
/* Return a description of the error that occurred in ACLSetUser() according to
* the errno value set by the function on error . */
2021-01-21 11:56:08 +02:00
const char * ACLSetUserStringError ( void ) {
const char * errmsg = " Wrong format " ;
2019-01-30 16:02:25 +01:00
if ( errno = = ENOENT )
errmsg = " Unknown command or category name in ACL " ;
else if ( errno = = EINVAL )
errmsg = " Syntax error " ;
else if ( errno = = EEXIST )
errmsg = " Adding a pattern after the * pattern (or the "
" 'allkeys' flag) is not valid and does not have any "
" effect. Try 'resetkeys' to start with an empty "
" list of patterns " ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
else if ( errno = = EISDIR )
errmsg = " Adding a pattern after the * pattern (or the "
" 'allchannels' flag) is not valid and does not have any "
" effect. Try 'resetchannels' to start with an empty "
" list of channels " ;
2019-02-11 17:00:51 +01:00
else if ( errno = = ENODEV )
errmsg = " The password you are trying to remove from the user does "
" not exist " ;
2019-09-17 03:32:35 -07:00
else if ( errno = = EBADMSG )
errmsg = " The password hash must be exactly 64 characters and contain "
" only lowercase hexadecimal characters " ;
2021-09-09 07:40:33 -07:00
else if ( errno = = EALREADY )
errmsg = " Duplicate user found. A user can only be defined once in "
" config files " ;
2022-01-22 13:09:40 +01:00
else if ( errno = = ECHILD )
errmsg = " Allowing first-arg of a subcommand is not supported " ;
2019-01-30 16:02:25 +01:00
return errmsg ;
}
2021-09-09 07:40:33 -07:00
/* Create the default user, this has special permissions. */
user * ACLCreateDefaultUser ( void ) {
user * new = ACLCreateUser ( " default " , 7 ) ;
ACLSetUser ( new , " +@all " , - 1 ) ;
ACLSetUser ( new , " ~* " , - 1 ) ;
ACLSetUser ( new , " &* " , - 1 ) ;
ACLSetUser ( new , " on " , - 1 ) ;
ACLSetUser ( new , " nopass " , - 1 ) ;
return new ;
2019-01-10 16:39:32 +01:00
}
2019-02-07 12:20:30 +01:00
/* Initialization of the ACL subsystem. */
void ACLInit ( void ) {
Users = raxNew ( ) ;
UsersToLoad = listCreate ( ) ;
2023-08-30 13:01:24 -07:00
ACLInitCommandCategories ( ) ;
2021-09-09 07:40:33 -07:00
listSetMatchMethod ( UsersToLoad , ACLListMatchLoadedUser ) ;
2020-01-27 18:37:52 +01:00
ACLLog = listCreate ( ) ;
2021-09-09 07:40:33 -07:00
DefaultUser = ACLCreateDefaultUser ( ) ;
2019-02-07 12:20:30 +01:00
}
2018-12-21 17:16:22 +01:00
/* Check the username and password pair and return C_OK if they are valid,
* otherwise C_ERR is returned and errno is set to :
*
* EINVAL : if the username - password do not match .
2023-09-08 21:10:17 +08:00
* ENOENT : if the specified user does not exist at all .
2018-12-21 17:16:22 +01:00
*/
int ACLCheckUserCredentials ( robj * username , robj * password ) {
2019-01-15 18:16:20 +01:00
user * u = ACLGetUserByName ( username - > ptr , sdslen ( username - > ptr ) ) ;
if ( u = = NULL ) {
2018-12-21 17:16:22 +01:00
errno = ENOENT ;
return C_ERR ;
}
2019-01-15 18:16:20 +01:00
/* Disabled users can't login. */
2019-01-31 16:49:22 +01:00
if ( u - > flags & USER_FLAG_DISABLED ) {
2018-12-21 17:16:22 +01:00
errno = EINVAL ;
return C_ERR ;
}
2019-01-15 18:16:20 +01:00
/* If the user is configured to don't require any password, we
* are already fine here . */
if ( u - > flags & USER_FLAG_NOPASS ) return C_OK ;
/* Check all the user passwords for at least one to match. */
listIter li ;
listNode * ln ;
listRewind ( u - > passwords , & li ) ;
2019-09-12 12:54:57 +02:00
sds hashed = ACLHashPassword ( password - > ptr , sdslen ( password - > ptr ) ) ;
2019-01-15 18:16:20 +01:00
while ( ( ln = listNext ( & li ) ) ) {
sds thispass = listNodeValue ( ln ) ;
2022-06-03 09:30:28 -07:00
if ( ! time_independent_strcmp ( hashed , thispass , HASH_PASSWORD_LEN ) ) {
2019-09-12 12:54:57 +02:00
sdsfree ( hashed ) ;
2019-01-15 18:16:20 +01:00
return C_OK ;
2019-09-12 12:54:57 +02:00
}
2019-01-15 18:16:20 +01:00
}
2019-09-12 12:54:57 +02:00
sdsfree ( hashed ) ;
2019-01-15 18:16:20 +01:00
/* If we reached this point, no password matched. */
errno = EINVAL ;
return C_ERR ;
2018-12-21 17:16:22 +01:00
}
2019-01-09 21:31:29 +01:00
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
/* If `err` is provided, this is added as an error reply to the client.
* Otherwise , the standard Auth error is added as a reply . */
void addAuthErrReply ( client * c , robj * err ) {
if ( clientHasPendingReplies ( c ) ) return ;
if ( ! err ) {
addReplyError ( c , " -WRONGPASS invalid username-password pair or user is disabled. " ) ;
return ;
}
addReplyError ( c , err - > ptr ) ;
}
2019-02-25 16:37:00 +01:00
/* This is like ACLCheckUserCredentials(), however if the user/pass
* are correct , the connection is put in authenticated state and the
* connection user reference is populated .
*
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
* The return value is AUTH_OK on success ( valid username / password pair ) & AUTH_ERR otherwise . */
int checkPasswordBasedAuth ( client * c , robj * username , robj * password ) {
2019-02-25 16:37:00 +01:00
if ( ACLCheckUserCredentials ( username , password ) = = C_OK ) {
c - > authenticated = 1 ;
c - > user = ACLGetUserByName ( username - > ptr , sdslen ( username - > ptr ) ) ;
2019-02-26 01:23:11 +00:00
moduleNotifyUserChanged ( c ) ;
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
return AUTH_OK ;
2019-02-25 16:37:00 +01:00
} else {
2021-09-23 08:52:56 +03:00
addACLLogEntry ( c , ACL_DENIED_AUTH , ( c - > flags & CLIENT_MULTI ) ? ACL_LOG_CTX_MULTI : ACL_LOG_CTX_TOPLEVEL , 0 , username - > ptr , NULL ) ;
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
return AUTH_ERR ;
2019-02-25 16:37:00 +01:00
}
}
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
/* Attempt authenticating the user - first through module based authentication,
* and then , if needed , with normal password based authentication .
* Returns one of the following codes :
* AUTH_OK - Indicates that authentication succeeded .
* AUTH_ERR - Indicates that authentication failed .
* AUTH_BLOCKED - Indicates module authentication is in progress through a blocking implementation .
*/
int ACLAuthenticateUser ( client * c , robj * username , robj * password , robj * * err ) {
int result = checkModuleAuthentication ( c , username , password , err ) ;
/* If authentication was not handled by any Module, attempt normal password based auth. */
if ( result = = AUTH_NOT_HANDLED ) {
result = checkPasswordBasedAuth ( c , username , password ) ;
}
return result ;
}
2019-01-09 21:31:29 +01:00
/* For ACL purposes, every user has a bitmap with the commands that such
* user is allowed to execute . In order to populate the bitmap , every command
* should have an assigned ID ( that is used to index the bitmap ) . This function
* creates such an ID : it uses sequential IDs , reusing the same ID for the same
* command name , so that a command retains the same ID in case of modules that
2022-01-23 16:05:06 +08:00
* are unloaded and later reloaded .
*
* The function does not take ownership of the ' cmdname ' SDS string .
* */
unsigned long ACLGetCommandID ( sds cmdname ) {
sds lowername = sdsdup ( cmdname ) ;
2019-01-18 13:24:53 +01:00
sdstolower ( lowername ) ;
2020-10-19 00:33:55 -04:00
if ( commandId = = NULL ) commandId = raxNew ( ) ;
2023-12-14 17:50:18 -05:00
void * id ;
if ( raxFind ( commandId , ( unsigned char * ) lowername , sdslen ( lowername ) , & id ) ) {
2019-01-18 13:24:53 +01:00
sdsfree ( lowername ) ;
return ( unsigned long ) id ;
}
2020-10-19 00:33:55 -04:00
raxInsert ( commandId , ( unsigned char * ) lowername , strlen ( lowername ) ,
2019-01-18 13:24:53 +01:00
( void * ) nextid , NULL ) ;
sdsfree ( lowername ) ;
2019-01-25 13:07:20 +01:00
unsigned long thisid = nextid ;
2019-01-23 08:10:57 +01:00
nextid + + ;
/* We never assign the last bit in the user commands bitmap structure,
* this way we can later check if this bit is set , understanding if the
* current ACL for the user was created starting with a + @ all to add all
* the possible commands and just subtracting other single commands or
* categories , or if , instead , the ACL was created just adding commands
* and command categories from scratch , not allowing future commands by
* default ( loaded via modules ) . This is useful when rewriting the ACLs
* with ACL SAVE . */
if ( nextid = = USER_COMMAND_BITS_COUNT - 1 ) nextid + + ;
2019-01-25 13:07:20 +01:00
return thisid ;
2019-01-09 21:31:29 +01:00
}
2019-01-10 16:33:48 +01:00
2020-10-19 00:33:55 -04:00
/* Clear command id table and reset nextid to 0. */
void ACLClearCommandID ( void ) {
if ( commandId ) raxFree ( commandId ) ;
commandId = NULL ;
nextid = 0 ;
}
2019-01-10 16:33:48 +01:00
/* Return an username by its name, or NULL if the user does not exist. */
user * ACLGetUserByName ( const char * name , size_t namelen ) {
2023-12-14 17:50:18 -05:00
void * myuser = NULL ;
raxFind ( Users , ( unsigned char * ) name , namelen , & myuser ) ;
2019-01-10 16:40:45 +01:00
return myuser ;
2019-01-10 16:33:48 +01:00
}
2019-01-10 16:35:55 +01:00
2022-01-20 13:05:27 -08:00
/* =============================================================================
* ACL permission checks
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* Check if the key can be accessed by the selector.
2021-09-23 08:52:56 +03:00
*
2022-01-20 13:05:27 -08:00
* If the selector can access the key , ACL_OK is returned , otherwise
2021-09-23 08:52:56 +03:00
* ACL_DENIED_KEY is returned . */
2022-01-20 13:05:27 -08:00
static int ACLSelectorCheckKey ( aclSelector * selector , const char * key , int keylen , int keyspec_flags ) {
/* The selector can access any key */
if ( selector - > flags & SELECTOR_FLAG_ALLKEYS ) return ACL_OK ;
2021-09-23 08:52:56 +03:00
listIter li ;
listNode * ln ;
2022-01-20 13:05:27 -08:00
listRewind ( selector - > patterns , & li ) ;
int key_flags = 0 ;
2024-05-08 20:58:53 +02:00
/* clang-format off */
2022-01-20 13:05:27 -08:00
if ( keyspec_flags & CMD_KEY_ACCESS ) key_flags | = ACL_READ_PERMISSION ;
if ( keyspec_flags & CMD_KEY_INSERT ) key_flags | = ACL_WRITE_PERMISSION ;
if ( keyspec_flags & CMD_KEY_DELETE ) key_flags | = ACL_WRITE_PERMISSION ;
if ( keyspec_flags & CMD_KEY_UPDATE ) key_flags | = ACL_WRITE_PERMISSION ;
2024-05-08 20:58:53 +02:00
/* clang-format on */
2021-09-23 08:52:56 +03:00
/* Test this key against every pattern. */
2022-01-20 13:05:27 -08:00
while ( ( ln = listNext ( & li ) ) ) {
keyPattern * pattern = listNodeValue ( ln ) ;
if ( ( pattern - > flags & key_flags ) ! = key_flags )
continue ;
size_t plen = sdslen ( pattern - > pattern ) ;
if ( stringmatchlen ( pattern - > pattern , plen , key , keylen , 0 ) )
return ACL_OK ;
}
return ACL_DENIED_KEY ;
}
2022-03-15 17:14:53 +02:00
/* Checks if the provided selector selector has access specified in flags
* to all keys in the keyspace . For example , CMD_KEY_READ access requires either
* ' % R ~ * ' , ' ~ * ' , or allkeys to be granted to the selector . Returns 1 if all
* the access flags are satisfied with this selector or 0 otherwise .
*/
static int ACLSelectorHasUnrestrictedKeyAccess ( aclSelector * selector , int flags ) {
/* The selector can access any key */
if ( selector - > flags & SELECTOR_FLAG_ALLKEYS ) return 1 ;
listIter li ;
listNode * ln ;
listRewind ( selector - > patterns , & li ) ;
int access_flags = 0 ;
2024-05-08 20:58:53 +02:00
/* clang-format off */
2022-03-15 17:14:53 +02:00
if ( flags & CMD_KEY_ACCESS ) access_flags | = ACL_READ_PERMISSION ;
if ( flags & CMD_KEY_INSERT ) access_flags | = ACL_WRITE_PERMISSION ;
if ( flags & CMD_KEY_DELETE ) access_flags | = ACL_WRITE_PERMISSION ;
if ( flags & CMD_KEY_UPDATE ) access_flags | = ACL_WRITE_PERMISSION ;
2024-05-08 20:58:53 +02:00
/* clang-format on */
2022-03-15 17:14:53 +02:00
/* Test this key against every pattern. */
while ( ( ln = listNext ( & li ) ) ) {
keyPattern * pattern = listNodeValue ( ln ) ;
if ( ( pattern - > flags & access_flags ) ! = access_flags )
continue ;
if ( ! strcmp ( pattern - > pattern , " * " ) ) {
return 1 ;
}
}
return 0 ;
}
2022-02-22 01:00:03 -08:00
/* Checks a channel against a provided list of channels. The is_pattern
* argument should only be used when subscribing ( not when publishing )
* and controls whether the input channel is evaluated as a channel pattern
* ( like in PSUBSCRIBE ) or a plain channel name ( like in SUBSCRIBE ) .
*
* Note that a plain channel name like in PUBLISH or SUBSCRIBE can be
* matched against ACL channel patterns , but the pattern provided in PSUBSCRIBE
* can only be matched as a literal against an ACL pattern ( using plain string compare ) . */
static int ACLCheckChannelAgainstList ( list * reference , const char * channel , int channellen , int is_pattern ) {
2022-01-20 13:05:27 -08:00
listIter li ;
listNode * ln ;
listRewind ( reference , & li ) ;
2021-09-23 08:52:56 +03:00
while ( ( ln = listNext ( & li ) ) ) {
sds pattern = listNodeValue ( ln ) ;
size_t plen = sdslen ( pattern ) ;
2022-02-22 01:00:03 -08:00
/* Channel patterns are matched literally against the channels in
* the list . Regular channels perform pattern matching . */
if ( ( is_pattern & & ! strcmp ( pattern , channel ) ) | |
( ! is_pattern & & stringmatchlen ( pattern , plen , channel , channellen , 0 ) ) )
2022-01-20 13:05:27 -08:00
{
2021-09-23 08:52:56 +03:00
return ACL_OK ;
2022-01-20 13:05:27 -08:00
}
2021-09-23 08:52:56 +03:00
}
2022-01-20 13:05:27 -08:00
return ACL_DENIED_CHANNEL ;
2021-09-23 08:52:56 +03:00
}
2022-01-20 13:05:27 -08:00
/* To prevent duplicate calls to getKeysResult, a cache is maintained
* in between calls to the various selectors . */
typedef struct {
int keys_init ;
getKeysResult keys ;
} aclKeyResultCache ;
void initACLKeyResultCache ( aclKeyResultCache * cache ) {
cache - > keys_init = 0 ;
}
void cleanupACLKeyResultCache ( aclKeyResultCache * cache ) {
if ( cache - > keys_init ) getKeysFreeResult ( & ( cache - > keys ) ) ;
}
/* Check if the command is ready to be executed according to the
* ACLs associated with the specified selector .
*
* If the selector can execute the command ACL_OK is returned , otherwise
* ACL_DENIED_CMD , ACL_DENIED_KEY , or ACL_DENIED_CHANNEL is returned : the first in case the
* command cannot be executed because the selector is not allowed to run such
* command , the second and third if the command is denied because the selector is trying
* to access a key or channel that are not among the specified patterns . */
2024-04-03 23:54:33 +07:00
static int ACLSelectorCheckCmd ( aclSelector * selector , struct serverCommand * cmd , robj * * argv , int argc , int * keyidxptr , aclKeyResultCache * cache ) {
2022-01-20 13:05:27 -08:00
uint64_t id = cmd - > id ;
int ret ;
if ( ! ( selector - > flags & SELECTOR_FLAG_ALLCOMMANDS ) & & ! ( cmd - > flags & CMD_NO_AUTH ) ) {
2019-01-14 18:35:21 +01:00
/* If the bit is not set we have to check further, in case the
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
* command is allowed just with that specific first argument . */
2022-01-20 13:05:27 -08:00
if ( ACLGetSelectorCommandBit ( selector , id ) = = 0 ) {
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
/* Check if the first argument matches. */
2021-09-23 08:52:56 +03:00
if ( argc < 2 | |
2022-01-20 13:05:27 -08:00
selector - > allowed_firstargs = = NULL | |
selector - > allowed_firstargs [ id ] = = NULL )
2019-01-28 18:15:59 +01:00
{
2019-01-16 18:31:05 +01:00
return ACL_DENIED_CMD ;
2019-01-28 18:15:59 +01:00
}
2019-01-14 18:35:21 +01:00
long subid = 0 ;
while ( 1 ) {
2022-01-20 13:05:27 -08:00
if ( selector - > allowed_firstargs [ id ] [ subid ] = = NULL )
2019-01-16 18:31:05 +01:00
return ACL_DENIED_CMD ;
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
int idx = cmd - > parent ? 2 : 1 ;
2022-01-20 13:05:27 -08:00
if ( ! strcasecmp ( argv [ idx ] - > ptr , selector - > allowed_firstargs [ id ] [ subid ] ) )
Treat subcommands as commands (#9504)
## Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
## Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
1. Dropped "ok-loading" in all except GET (this doesn't change behavior since
there were checks in the code doing that)
XINFO
Used to have "read-only random"
Changes:
1. Dropped "random" in all except CONSUMERS
XGROUP
Used to have "write use-memory"
Changes:
1. Dropped "use-memory" in all except CREATE and CREATECONSUMER
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
1. Dropped "random" in PURGE and USAGE
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
1. Dropped "admin" in WHOAMI, GENPASS, and CAT
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
1. Dropped "random" in RESET
OBJECT
Used to have "read-only random"
Changes:
1. Dropped "random" in ENCODING and REFCOUNT
SCRIPT
Used to have "may-replicate no-script"
Changes:
1. Dropped "may-replicate" in all except FLUSH and LOAD
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
1. Dropped "random" in all except INFO and LIST
2. Dropped "admin" in ID, TRACKING, CACHING, GETREDIR, INFO, SETNAME, GETNAME, and REPLY
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
1. Dropped "admin in countkeysinslots, getkeysinslot, info, nodes, keyslot, myid, and slots
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
## New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
```
COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)]
```
which returns a set of all commands (unless filters), but excluding subcommands.
## Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
## ACL changes:
1. Now, that each subcommand is actually a command, each has its own ACL id.
2. The old mechanism of allowed_subcommands is redundant
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
3. I have renamed allowed_subcommands to allowed_firstargs to emphasize the difference.
4. Because subcommands are commands in ACL too, you can now use "-" to block subcommands
(e.g. "+client -client|kill"), which wasn't possible in the past.
5. It is also possible to use the allowed_firstargs mechanism with subcommand.
For example: `+config -config|set +config|set|loglevel` will block all CONFIG SET except
for setting the log level.
6. All of the ACL changes above required some amount of refactoring.
## Misc
1. There are two approaches: Either each subcommand has its own function or all
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
2. Deleted memoryGetKeys: It is no longer needed because MEMORY USAGE now uses the "range" key spec.
4. Bugfix: GETNAME was missing from CLIENT's help message.
5. Sentinel and Redis now use the same table, with the same function pointer.
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
6. Command stats now show the stats per subcommand (e.g. instead of stats just
for "config" you will have stats for "config|set", "config|get", etc.)
7. It is now possible to use COMMAND directly on subcommands:
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
8. STRALGO is now a container command (has "help")
## Breaking changes:
1. Command stats now show the stats per subcommand (see (5) above)
2021-10-20 10:52:57 +02:00
break ; /* First argument match found. Stop here. */
2019-01-14 18:35:21 +01:00
subid + + ;
}
}
2019-01-14 13:19:50 +01:00
}
2019-01-14 18:35:21 +01:00
/* Check if the user can execute commands explicitly touching the keys
2022-01-20 13:05:27 -08:00
* mentioned in the command arguments . */
if ( ! ( selector - > flags & SELECTOR_FLAG_ALLKEYS ) & & doesCommandHaveKeys ( cmd ) ) {
if ( ! ( cache - > keys_init ) ) {
cache - > keys = ( getKeysResult ) GETKEYS_RESULT_INIT ;
getKeysFromCommandWithSpecs ( cmd , argv , argc , GET_KEYSPEC_DEFAULT , & ( cache - > keys ) ) ;
cache - > keys_init = 1 ;
}
getKeysResult * result = & ( cache - > keys ) ;
keyReference * resultidx = result - > keys ;
for ( int j = 0 ; j < result - > numkeys ; j + + ) {
int idx = resultidx [ j ] . pos ;
ret = ACLSelectorCheckKey ( selector , argv [ idx ] - > ptr , sdslen ( argv [ idx ] - > ptr ) , resultidx [ j ] . flags ) ;
2021-09-23 08:52:56 +03:00
if ( ret ! = ACL_OK ) {
2022-02-22 01:00:03 -08:00
if ( keyidxptr ) * keyidxptr = resultidx [ j ] . pos ;
2021-09-23 08:52:56 +03:00
return ret ;
2019-01-25 19:35:18 +08:00
}
2019-01-16 13:39:04 +01:00
}
2019-01-14 13:19:50 +01:00
}
2022-01-20 13:05:27 -08:00
/* Check if the user can execute commands explicitly touching the channels
* mentioned in the command arguments */
2022-02-22 01:00:03 -08:00
const int channel_flags = CMD_CHANNEL_PUBLISH | CMD_CHANNEL_SUBSCRIBE ;
if ( ! ( selector - > flags & SELECTOR_FLAG_ALLCHANNELS ) & & doesCommandHaveChannelsWithFlags ( cmd , channel_flags ) ) {
getKeysResult channels = ( getKeysResult ) GETKEYS_RESULT_INIT ;
getChannelsFromCommand ( cmd , argv , argc , & channels ) ;
keyReference * channelref = channels . keys ;
for ( int j = 0 ; j < channels . numkeys ; j + + ) {
int idx = channelref [ j ] . pos ;
if ( ! ( channelref [ j ] . flags & channel_flags ) ) continue ;
int is_pattern = channelref [ j ] . flags & CMD_CHANNEL_PATTERN ;
int ret = ACLCheckChannelAgainstList ( selector - > channels , argv [ idx ] - > ptr , sdslen ( argv [ idx ] - > ptr ) , is_pattern ) ;
if ( ret ! = ACL_OK ) {
if ( keyidxptr ) * keyidxptr = channelref [ j ] . pos ;
getKeysFreeResult ( & channels ) ;
return ret ;
}
2022-01-20 13:05:27 -08:00
}
2022-02-22 01:00:03 -08:00
getKeysFreeResult ( & channels ) ;
2022-01-20 13:05:27 -08:00
}
2019-01-16 18:31:05 +01:00
return ACL_OK ;
2019-01-14 13:19:50 +01:00
}
2022-01-20 13:05:27 -08:00
/* Check if the key can be accessed by the client according to
2022-02-22 01:00:03 -08:00
* the ACLs associated with the specified user according to the
* keyspec access flags .
2022-01-20 13:05:27 -08:00
*
* If the user can access the key , ACL_OK is returned , otherwise
* ACL_DENIED_KEY is returned . */
int ACLUserCheckKeyPerm ( user * u , const char * key , int keylen , int flags ) {
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
listIter li ;
listNode * ln ;
2022-01-20 13:05:27 -08:00
/* If there is no associated user, the connection can run anything. */
if ( u = = NULL ) return ACL_OK ;
/* Check all of the selectors */
listRewind ( u - > selectors , & li ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
while ( ( ln = listNext ( & li ) ) ) {
2022-01-20 13:05:27 -08:00
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
if ( ACLSelectorCheckKey ( s , key , keylen , flags ) = = ACL_OK ) {
return ACL_OK ;
}
}
return ACL_DENIED_KEY ;
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
2022-03-15 17:14:53 +02:00
/* Checks if the user can execute the given command with the added restriction
* it must also have the access specified in flags to any key in the key space .
* For example , CMD_KEY_READ access requires either ' % R ~ * ' , ' ~ * ' , or allkeys to be
* granted in addition to the access required by the command . Returns 1
* if the user has access or 0 otherwise .
*/
2024-04-03 23:54:33 +07:00
int ACLUserCheckCmdWithUnrestrictedKeyAccess ( user * u , struct serverCommand * cmd , robj * * argv , int argc , int flags ) {
2022-03-15 17:14:53 +02:00
listIter li ;
listNode * ln ;
int local_idxptr ;
/* If there is no associated user, the connection can run anything. */
if ( u = = NULL ) return 1 ;
/* For multiple selectors, we cache the key result in between selector
* calls to prevent duplicate lookups . */
aclKeyResultCache cache ;
initACLKeyResultCache ( & cache ) ;
/* Check each selector sequentially */
listRewind ( u - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
int acl_retval = ACLSelectorCheckCmd ( s , cmd , argv , argc , & local_idxptr , & cache ) ;
if ( acl_retval = = ACL_OK & & ACLSelectorHasUnrestrictedKeyAccess ( s , flags ) ) {
cleanupACLKeyResultCache ( & cache ) ;
return 1 ;
}
}
cleanupACLKeyResultCache ( & cache ) ;
return 0 ;
}
2022-01-20 13:05:27 -08:00
/* Check if the channel can be accessed by the client according to
* the ACLs associated with the specified user .
*
* If the user can access the key , ACL_OK is returned , otherwise
* ACL_DENIED_CHANNEL is returned . */
2022-02-22 01:00:03 -08:00
int ACLUserCheckChannelPerm ( user * u , sds channel , int is_pattern ) {
2022-01-20 13:05:27 -08:00
listIter li ;
listNode * ln ;
/* If there is no associated user, the connection can run anything. */
if ( u = = NULL ) return ACL_OK ;
/* Check all of the selectors */
listRewind ( u - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
/* The selector can run any keys */
if ( s - > flags & SELECTOR_FLAG_ALLCHANNELS ) return ACL_OK ;
/* Otherwise, loop over the selectors list and check each channel */
2022-02-22 01:00:03 -08:00
if ( ACLCheckChannelAgainstList ( s - > channels , channel , sdslen ( channel ) , is_pattern ) = = ACL_OK ) {
2022-01-20 13:05:27 -08:00
return ACL_OK ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
}
2022-01-20 13:05:27 -08:00
return ACL_DENIED_CHANNEL ;
}
Unify ACL failure error messaging. (#11160)
Motivation: for applications that use RM ACL verification functions, they would
want to return errors back to the user, in ways that are consistent with Redis.
While investigating how we should return ACL errors to the user, we realized that
Redis isn't consistent, and currently returns ACL error strings in 3 primary ways.
[For the actual implications of this change, see the "Impact" section at the bottom]
1. how it returns an error when calling a command normally
ACL_DENIED_CMD -> "this user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "this user has no permissions to access one of the keys used as arguments"
ACL_DENIED_CHANNEL -> "this user has no permissions to access one of the channels used as arguments"
2. how it returns an error when calling via 'acl dryrun' command
ACL_DENIED_CMD -> "This user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "This user has no permissions to access the '%s' key"
ACL_DENIED_CHANNEL -> "This user has no permissions to access the '%s' channel"
3. how it returns an error via RM_Call (and scripting is similar).
ACL_DENIED_CMD -> "can't run this command or subcommand";
ACL_DENIED_KEY -> "can't access at least one of the keys mentioned in the command arguments";
ACL_DENIED_CHANNEL -> "can't publish to the channel mentioned in the command";
In addition, if one wants to use RM_Call's "dry run" capability instead of the RM ACL
functions directly, one also sees a different problem than it returns ACL errors with a -ERR,
not a -PERM, so it can't be returned directly to the caller.
This PR modifies the code to generate a base message in a common manner with the ability
to set verbose flag for acl dry run errors, and keep it unset for normal/rm_call/script cases
```c
sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose) {
switch (acl_res) {
case ACL_DENIED_CMD:
return sdscatfmt(sdsempty(), "User %S has no permissions to run "
"the '%S' command", user->name, cmd->fullname);
case ACL_DENIED_KEY:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' key", user->name, errored_val);
} else {
return sdsnew("No permissions to access a key");
}
case ACL_DENIED_CHANNEL:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' channel", user->name, errored_val);
} else {
return sdsnew("No permissions to access a channel");
}
}
```
The caller can append/prepend the message (adding NOPERM for normal/RM_Call or indicating it's within a script).
Impact:
- Plain commands, as well as scripts and RM_Call now include the user name.
- ACL DRYRUN remains the only one that's verbose (mentions the offending channel or key name)
- Changes RM_Call ACL errors from being a `-ERR` to being `-NOPERM` (besides for textual changes)
**This somewhat a breaking change, but it only affects the RM_Call with both `C` and `E`, or `D`**
- Changes ACL errors in scripts textually from being
`The user executing the script <old non unified text>`
to
`ACL failure in script: <new unified text>`
2022-10-16 09:01:37 +03:00
/* Lower level API that checks if a specified user is able to execute a given command.
*
* If the command fails an ACL check , idxptr will be to set to the first argv entry that
* causes the failure , either 0 if the command itself fails or the idx of the key / channel
* that causes the failure */
2024-04-03 23:54:33 +07:00
int ACLCheckAllUserCommandPerm ( user * u , struct serverCommand * cmd , robj * * argv , int argc , int * idxptr ) {
2022-01-20 13:05:27 -08:00
listIter li ;
listNode * ln ;
/* If there is no associated user, the connection can run anything. */
if ( u = = NULL ) return ACL_OK ;
/* We have to pick a single error to log, the logic for picking is as follows:
* 1 ) If no selector can execute the command , return the command .
* 2 ) Return the last key or channel that no selector could match . */
int relevant_error = ACL_DENIED_CMD ;
int local_idxptr = 0 , last_idx = 0 ;
/* For multiple selectors, we cache the key result in between selector
* calls to prevent duplicate lookups . */
aclKeyResultCache cache ;
initACLKeyResultCache ( & cache ) ;
/* Check each selector sequentially */
listRewind ( u - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
int acl_retval = ACLSelectorCheckCmd ( s , cmd , argv , argc , & local_idxptr , & cache ) ;
if ( acl_retval = = ACL_OK ) {
cleanupACLKeyResultCache ( & cache ) ;
return ACL_OK ;
}
if ( acl_retval > relevant_error | |
( acl_retval = = relevant_error & & local_idxptr > last_idx ) )
{
relevant_error = acl_retval ;
last_idx = local_idxptr ;
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-01-20 13:05:27 -08:00
* idxptr = last_idx ;
cleanupACLKeyResultCache ( & cache ) ;
return relevant_error ;
}
/* High level API for checking if a client can execute the queued up command */
int ACLCheckAllPerm ( client * c , int * idxptr ) {
return ACLCheckAllUserCommandPerm ( c - > user , c - > cmd , c - > argv , c - > argc , idxptr ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2023-12-24 11:56:44 +02:00
/* If 'new' can access all channels 'original' could then return NULL;
Otherwise return a list of channels that the new user can access */
list * getUpcomingChannelList ( user * new , user * original ) {
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
listIter li , lpi ;
listNode * ln , * lpn ;
2023-12-24 11:56:44 +02:00
/* Optimization: we check if any selector has all channel permissions. */
2022-01-20 13:05:27 -08:00
listRewind ( new - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
2023-12-24 11:56:44 +02:00
if ( s - > flags & SELECTOR_FLAG_ALLCHANNELS ) return NULL ;
2022-01-20 13:05:27 -08:00
}
2023-12-24 11:56:44 +02:00
/* Next, check if the new list of channels
2022-01-20 13:05:27 -08:00
* is a strict superset of the original . This is done by
* created an " upcoming " list of all channels that are in
* the new user and checking each of the existing channels
* against it . */
list * upcoming = listCreate ( ) ;
listRewind ( new - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
listRewind ( s - > channels , & lpi ) ;
while ( ( lpn = listNext ( & lpi ) ) ) {
listAddNodeTail ( upcoming , listNodeValue ( lpn ) ) ;
}
}
int match = 1 ;
listRewind ( original - > selectors , & li ) ;
while ( ( ln = listNext ( & li ) ) & & match ) {
aclSelector * s = ( aclSelector * ) listNodeValue ( ln ) ;
2023-04-02 16:18:28 +03:00
/* If any of the original selectors has the all-channels permission, but
* the new ones don ' t ( this is checked earlier in this function ) , then the
* new list is not a strict superset of the original . */
if ( s - > flags & SELECTOR_FLAG_ALLCHANNELS ) {
match = 0 ;
break ;
}
2022-01-20 13:05:27 -08:00
listRewind ( s - > channels , & lpi ) ;
while ( ( lpn = listNext ( & lpi ) ) & & match ) {
if ( ! listSearchKey ( upcoming , listNodeValue ( lpn ) ) ) {
match = 0 ;
break ;
}
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-01-20 13:05:27 -08:00
if ( match ) {
/* All channels were matched, no need to kill clients. */
listRelease ( upcoming ) ;
2023-12-24 11:56:44 +02:00
return NULL ;
2022-01-20 13:05:27 -08:00
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
2023-12-24 11:56:44 +02:00
return upcoming ;
}
/* Check if the client should be killed because it is subscribed to channels that were
* permitted in the past , are not in the ` upcoming ` channel list . */
int ACLShouldKillPubsubClient ( client * c , list * upcoming ) {
robj * o ;
int kill = 0 ;
if ( getClientType ( c ) = = CLIENT_TYPE_PUBSUB ) {
/* Check for pattern violations. */
dictIterator * di = dictGetIterator ( c - > pubsub_patterns ) ;
dictEntry * de ;
while ( ! kill & & ( ( de = dictNext ( di ) ) ! = NULL ) ) {
o = dictGetKey ( de ) ;
int res = ACLCheckChannelAgainstList ( upcoming , o - > ptr , sdslen ( o - > ptr ) , 1 ) ;
kill = ( res = = ACL_DENIED_CHANNEL ) ;
}
dictReleaseIterator ( di ) ;
/* Check for channel violations. */
if ( ! kill ) {
/* Check for global channels violation. */
di = dictGetIterator ( c - > pubsub_channels ) ;
2023-06-19 21:31:18 +08:00
while ( ! kill & & ( ( de = dictNext ( di ) ) ! = NULL ) ) {
o = dictGetKey ( de ) ;
2023-12-24 11:56:44 +02:00
int res = ACLCheckChannelAgainstList ( upcoming , o - > ptr , sdslen ( o - > ptr ) , 0 ) ;
2022-01-20 13:05:27 -08:00
kill = ( res = = ACL_DENIED_CHANNEL ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2023-06-19 21:31:18 +08:00
dictReleaseIterator ( di ) ;
2023-12-24 11:56:44 +02:00
}
if ( ! kill ) {
/* Check for shard channels violation. */
di = dictGetIterator ( c - > pubsubshard_channels ) ;
while ( ! kill & & ( ( de = dictNext ( di ) ) ! = NULL ) ) {
o = dictGetKey ( de ) ;
int res = ACLCheckChannelAgainstList ( upcoming , o - > ptr , sdslen ( o - > ptr ) , 0 ) ;
kill = ( res = = ACL_DENIED_CHANNEL ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2023-12-24 11:56:44 +02:00
dictReleaseIterator ( di ) ;
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
2023-12-24 11:56:44 +02:00
if ( kill ) {
return 1 ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
}
2023-12-24 11:56:44 +02:00
return 0 ;
}
/* Check if the user's existing pub/sub clients violate the ACL pub/sub
* permissions specified via the upcoming argument , and kill them if so . */
void ACLKillPubsubClientsIfNeeded ( user * new , user * original ) {
/* Do nothing if there are no subscribers. */
Refactor the per-slot dict-array db.c into a new kvstore data structure (#12822)
# Description
Gather most of the scattered `redisDb`-related code from the per-slot
dict PR (#11695) and turn it to a new data structure, `kvstore`. i.e.
it's a class that represents an array of dictionaries.
# Motivation
The main motivation is code cleanliness, the idea of using an array of
dictionaries is very well-suited to becoming a self-contained data
structure.
This allowed cleaning some ugly code, among others: loops that run twice
on the main dict and expires dict, and duplicate code for allocating and
releasing this data structure.
# Notes
1. This PR reverts the part of https://github.com/redis/redis/pull/12848
where the `rehashing` list is global (handling rehashing `dict`s is
under the responsibility of `kvstore`, and should not be managed by the
server)
2. This PR also replaces the type of `server.pubsubshard_channels` from
`dict**` to `kvstore` (original PR:
https://github.com/redis/redis/pull/12804). After that was done,
server.pubsub_channels was also chosen to be a `kvstore` (with only one
`dict`, which seems odd) just to make the code cleaner by making it the
same type as `server.pubsubshard_channels`, see
`pubsubtype.serverPubSubChannels`
3. the keys and expires kvstores are currenlty configured to allocate
the individual dicts only when the first key is added (unlike before, in
which they allocated them in advance), but they won't release them when
the last key is deleted.
Worth mentioning that due to the recent change the reply of DEBUG
HTSTATS changed, in case no keys were ever added to the db.
before:
```
127.0.0.1:6379> DEBUG htstats 9
[Dictionary HT]
Hash table 0 stats (main hash table):
No stats available for empty dictionaries
[Expires HT]
Hash table 0 stats (main hash table):
No stats available for empty dictionaries
```
after:
```
127.0.0.1:6379> DEBUG htstats 9
[Dictionary HT]
[Expires HT]
```
2024-02-05 22:21:35 +07:00
if ( pubsubTotalSubscriptions ( ) = = 0 )
2023-12-24 11:56:44 +02:00
return ;
list * channels = getUpcomingChannelList ( new , original ) ;
/* If the new user's pubsub permissions are a strict superset of the original, return early. */
if ( ! channels )
return ;
listIter li ;
listNode * ln ;
/* Permissions have changed, so we need to iterate through all
* the clients and disconnect those that are no longer valid .
* Scan all connected clients to find the user ' s pub / subs . */
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
client * c = listNodeValue ( ln ) ;
if ( c - > user ! = original )
continue ;
if ( ACLShouldKillPubsubClient ( c , channels ) )
freeClient ( c ) ;
}
listRelease ( channels ) ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-01-20 13:05:27 -08:00
/* =============================================================================
* ACL loading / saving functions
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* Selector definitions should be sent as a single argument, however
* we will be lenient and try to find selector definitions spread
* across multiple arguments since it makes for a simpler user experience
* for ACL SETUSER as well as when loading from conf files .
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
*
2022-01-20 13:05:27 -08:00
* This function takes in an array of ACL operators , excluding the username ,
* and merges selector operations that are spread across multiple arguments . The return
* value is a new SDS array , with length set to the passed in merged_argc . Arguments
* that are untouched are still duplicated . If there is an unmatched parenthesis , NULL
* is returned and invalid_idx is set to the argument with the start of the opening
* parenthesis . */
sds * ACLMergeSelectorArguments ( sds * argv , int argc , int * merged_argc , int * invalid_idx ) {
* merged_argc = 0 ;
int open_bracket_start = - 1 ;
sds * acl_args = ( sds * ) zmalloc ( sizeof ( sds ) * argc ) ;
sds selector = NULL ;
for ( int j = 0 ; j < argc ; j + + ) {
char * op = argv [ j ] ;
2023-08-02 15:46:06 +08:00
if ( open_bracket_start = = - 1 & &
( op [ 0 ] = = ' ( ' & & op [ sdslen ( op ) - 1 ] ! = ' ) ' ) ) {
2022-01-20 13:05:27 -08:00
selector = sdsdup ( argv [ j ] ) ;
open_bracket_start = j ;
continue ;
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
2022-01-20 13:05:27 -08:00
if ( open_bracket_start ! = - 1 ) {
selector = sdscatfmt ( selector , " %s " , op ) ;
if ( op [ sdslen ( op ) - 1 ] = = ' ) ' ) {
open_bracket_start = - 1 ;
acl_args [ * merged_argc ] = selector ;
( * merged_argc ) + + ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-01-20 13:05:27 -08:00
continue ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
}
2022-01-20 13:05:27 -08:00
acl_args [ * merged_argc ] = sdsdup ( argv [ j ] ) ;
( * merged_argc ) + + ;
}
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
2022-01-20 13:05:27 -08:00
if ( open_bracket_start ! = - 1 ) {
for ( int i = 0 ; i < * merged_argc ; i + + ) sdsfree ( acl_args [ i ] ) ;
zfree ( acl_args ) ;
sdsfree ( selector ) ;
if ( invalid_idx ) * invalid_idx = open_bracket_start ;
return NULL ;
}
2021-03-26 19:10:01 +08:00
2022-01-20 13:05:27 -08:00
return acl_args ;
2021-09-23 08:52:56 +03:00
}
2022-09-22 16:29:00 +03:00
/* takes an acl string already split on spaces and adds it to the given user
* if the user object is NULL , will create a user with the given username
*
* Returns an error as an sds string if the ACL string is not parsable
*/
sds ACLStringSetUser ( user * u , sds username , sds * argv , int argc ) {
serverAssert ( u ! = NULL | | username ! = NULL ) ;
sds error = NULL ;
int merged_argc = 0 , invalid_idx = 0 ;
sds * acl_args = ACLMergeSelectorArguments ( argv , argc , & merged_argc , & invalid_idx ) ;
if ( ! acl_args ) {
return sdscatfmt ( sdsempty ( ) ,
" Unmatched parenthesis in acl selector starting "
" at '%s'. " , ( char * ) argv [ invalid_idx ] ) ;
}
/* Create a temporary user to validate and stage all changes against
* before applying to an existing user or creating a new user . If all
* arguments are valid the user parameters will all be applied together .
* If there are any errors then none of the changes will be applied . */
user * tempu = ACLCreateUnlinkedUser ( ) ;
if ( u ) {
ACLCopyUser ( tempu , u ) ;
}
for ( int j = 0 ; j < merged_argc ; j + + ) {
if ( ACLSetUser ( tempu , acl_args [ j ] , ( ssize_t ) sdslen ( acl_args [ j ] ) ) ! = C_OK ) {
const char * errmsg = ACLSetUserStringError ( ) ;
error = sdscatfmt ( sdsempty ( ) ,
" Error in ACL SETUSER modifier '%s': %s " ,
( char * ) acl_args [ j ] , errmsg ) ;
goto cleanup ;
}
}
/* Existing pub/sub clients authenticated with the user may need to be
* disconnected if ( some of ) their channel permissions were revoked . */
if ( u ) {
ACLKillPubsubClientsIfNeeded ( tempu , u ) ;
}
/* Overwrite the user with the temporary user we modified above. */
if ( ! u ) {
u = ACLCreateUser ( username , sdslen ( username ) ) ;
}
serverAssert ( u ! = NULL ) ;
ACLCopyUser ( u , tempu ) ;
cleanup :
ACLFreeUser ( tempu ) ;
for ( int i = 0 ; i < merged_argc ; i + + ) {
sdsfree ( acl_args [ i ] ) ;
}
zfree ( acl_args ) ;
return error ;
}
2019-02-01 12:20:09 +01:00
/* Given an argument vector describing a user in the form:
*
* user < username > . . . ACL rules and flags . . .
*
* this function validates , and if the syntax is valid , appends
* the user definition to a list for later loading .
*
* The rules are tested for validity and if there obvious syntax errors
* the function returns C_ERR and does nothing , otherwise C_OK is returned
* and the user is appended to the list .
*
* Note that this function cannot stop in case of commands that are not found
* and , in that case , the error will be emitted later , because certain
2019-02-04 13:00:38 +01:00
* commands may be defined later once modules are loaded .
*
* When an error is detected and C_ERR is returned , the function populates
* by reference ( if not set to NULL ) the argc_err argument with the index
* of the argv vector that caused the error . */
int ACLAppendUserForLoading ( sds * argv , int argc , int * argc_err ) {
if ( argc < 2 | | strcasecmp ( argv [ 0 ] , " user " ) ) {
if ( argc_err ) * argc_err = 0 ;
return C_ERR ;
}
2019-02-01 13:02:41 +01:00
2021-09-09 07:40:33 -07:00
if ( listSearchKey ( UsersToLoad , argv [ 1 ] ) ) {
if ( argc_err ) * argc_err = 1 ;
errno = EALREADY ;
return C_ERR ;
}
2022-01-20 13:05:27 -08:00
/* Merged selectors before trying to process */
int merged_argc ;
sds * acl_args = ACLMergeSelectorArguments ( argv + 2 , argc - 2 , & merged_argc , argc_err ) ;
if ( ! acl_args ) {
return C_ERR ;
}
2023-06-11 09:11:16 +08:00
/* Try to apply the user rules in a fake user to see if they
* are actually valid . */
user * fakeuser = ACLCreateUnlinkedUser ( ) ;
2022-01-20 13:05:27 -08:00
for ( int j = 0 ; j < merged_argc ; j + + ) {
if ( ACLSetUser ( fakeuser , acl_args [ j ] , sdslen ( acl_args [ j ] ) ) = = C_ERR ) {
2019-02-04 12:55:26 +01:00
if ( errno ! = ENOENT ) {
ACLFreeUser ( fakeuser ) ;
2019-02-04 13:00:38 +01:00
if ( argc_err ) * argc_err = j ;
2022-01-20 13:05:27 -08:00
for ( int i = 0 ; i < merged_argc ; i + + ) sdsfree ( acl_args [ i ] ) ;
zfree ( acl_args ) ;
2019-02-04 12:55:26 +01:00
return C_ERR ;
}
2019-02-01 13:02:41 +01:00
}
}
/* Rules look valid, let's append the user to the list. */
2022-01-20 13:05:27 -08:00
sds * copy = zmalloc ( sizeof ( sds ) * ( merged_argc + 2 ) ) ;
copy [ 0 ] = sdsdup ( argv [ 1 ] ) ;
for ( int j = 0 ; j < merged_argc ; j + + ) copy [ j + 1 ] = sdsdup ( acl_args [ j ] ) ;
copy [ merged_argc + 1 ] = NULL ;
2019-02-01 13:02:41 +01:00
listAddNodeTail ( UsersToLoad , copy ) ;
2019-02-04 12:55:26 +01:00
ACLFreeUser ( fakeuser ) ;
2022-01-20 13:05:27 -08:00
for ( int i = 0 ; i < merged_argc ; i + + ) sdsfree ( acl_args [ i ] ) ;
zfree ( acl_args ) ;
2019-02-01 13:02:41 +01:00
return C_OK ;
2019-02-01 12:20:09 +01:00
}
2019-02-04 16:35:15 +01:00
/* This function will load the configured users appended to the server
* configuration via ACLAppendUserForLoading ( ) . On loading errors it will
* log an error and return C_ERR , otherwise C_OK will be returned . */
int ACLLoadConfiguredUsers ( void ) {
listIter li ;
listNode * ln ;
listRewind ( UsersToLoad , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
sds * aclrules = listNodeValue ( ln ) ;
2019-02-05 10:52:05 +01:00
sds username = aclrules [ 0 ] ;
2020-04-15 16:39:42 +02:00
if ( ACLStringHasSpaces ( aclrules [ 0 ] , sdslen ( aclrules [ 0 ] ) ) ) {
serverLog ( LL_WARNING , " Spaces not allowed in ACL usernames " ) ;
return C_ERR ;
}
2019-02-05 10:52:05 +01:00
user * u = ACLCreateUser ( username , sdslen ( username ) ) ;
2019-02-04 16:35:15 +01:00
if ( ! u ) {
2021-09-09 07:40:33 -07:00
/* Only valid duplicate user is the default one. */
serverAssert ( ! strcmp ( username , " default " ) ) ;
u = ACLGetUserByName ( " default " , 7 ) ;
2019-02-05 10:52:05 +01:00
ACLSetUser ( u , " reset " , - 1 ) ;
2019-02-04 16:35:15 +01:00
}
/* Load every rule defined for this user. */
for ( int j = 1 ; aclrules [ j ] ; j + + ) {
if ( ACLSetUser ( u , aclrules [ j ] , sdslen ( aclrules [ j ] ) ) ! = C_OK ) {
2021-01-21 11:56:08 +02:00
const char * errmsg = ACLSetUserStringError ( ) ;
2019-02-04 16:35:15 +01:00
serverLog ( LL_WARNING , " Error loading ACL rule '%s' for "
" the user named '%s': %s " ,
2019-02-04 16:58:35 +01:00
aclrules [ j ] , aclrules [ 0 ] , errmsg ) ;
2019-02-04 16:35:15 +01:00
return C_ERR ;
}
}
/* Having a disabled user in the configuration may be an error,
* warn about it without returning any error to the caller . */
if ( u - > flags & USER_FLAG_DISABLED ) {
serverLog ( LL_NOTICE , " The user '%s' is disabled (there is no "
" 'on' modifier in the user description). Make "
" sure this is not a configuration error. " ,
aclrules [ 0 ] ) ;
}
}
return C_OK ;
}
2019-02-06 12:39:11 +01:00
/* This function loads the ACL from the specified filename: every line
2019-06-07 13:20:22 -07:00
* is validated and should be either empty or in the format used to specify
2024-04-03 10:47:26 -07:00
* users in the valkey . conf or in the ACL file , that is :
2019-02-06 12:39:11 +01:00
*
* user < username > . . . rules . . .
*
* Note that this function considers comments starting with ' # ' as errors
* because the ACL file is meant to be rewritten , and comments would be
* lost after the rewrite . Yet empty lines are allowed to avoid being too
* strict .
*
* One important part of implementing ACL LOAD , that uses this function , is
* to avoid ending with broken rules if the ACL file is invalid for some
* reason , so the function will attempt to validate the rules before loading
* each user . For every line that will be found broken the function will
2019-02-07 12:57:21 +01:00
* collect an error message .
*
* IMPORTANT : If there is at least a single error , nothing will be loaded
* and the rules will remain exactly as they were .
2019-02-06 12:39:11 +01:00
*
* At the end of the process , if no errors were found in the whole file then
* NULL is returned . Otherwise an SDS string describing in a single line
* a description of all the issues found is returned . */
sds ACLLoadFromFile ( const char * filename ) {
FILE * fp ;
char buf [ 1024 ] ;
/* Open the ACL file. */
if ( ( fp = fopen ( filename , " r " ) ) = = NULL ) {
sds errors = sdscatprintf ( sdsempty ( ) ,
" Error loading ACLs, opening file '%s': %s " ,
filename , strerror ( errno ) ) ;
return errors ;
}
/* Load the whole file as a single string in memory. */
sds acls = sdsempty ( ) ;
2019-02-07 16:53:35 +01:00
while ( fgets ( buf , sizeof ( buf ) , fp ) ! = NULL )
2019-02-06 12:39:11 +01:00
acls = sdscat ( acls , buf ) ;
fclose ( fp ) ;
/* Split the file into lines and attempt to load each line. */
int totlines ;
sds * lines , errors = sdsempty ( ) ;
lines = sdssplitlen ( acls , strlen ( acls ) , " \n " , 1 , & totlines ) ;
2019-02-21 17:03:06 +01:00
sdsfree ( acls ) ;
2019-02-06 12:39:11 +01:00
2019-06-07 13:20:22 -07:00
/* We do all the loading in a fresh instance of the Users radix tree,
2019-02-07 12:57:21 +01:00
* so if there are errors loading the ACL file we can rollback to the
* old version . */
rax * old_users = Users ;
Users = raxNew ( ) ;
/* Load each line of the file. */
2019-02-06 12:39:11 +01:00
for ( int i = 0 ; i < totlines ; i + + ) {
sds * argv ;
int argc ;
int linenum = i + 1 ;
lines [ i ] = sdstrim ( lines [ i ] , " \t \r \n " ) ;
/* Skip blank lines */
if ( lines [ i ] [ 0 ] = = ' \0 ' ) continue ;
/* Split into arguments */
2020-05-29 11:07:13 +02:00
argv = sdssplitlen ( lines [ i ] , sdslen ( lines [ i ] ) , " " , 1 , & argc ) ;
2019-02-06 12:39:11 +01:00
if ( argv = = NULL ) {
errors = sdscatprintf ( errors ,
2019-02-07 17:07:35 +01:00
" %s:%d: unbalanced quotes in acl line. " ,
server . acl_filename , linenum ) ;
2019-02-06 12:39:11 +01:00
continue ;
}
/* Skip this line if the resulting command vector is empty. */
if ( argc = = 0 ) {
sdsfreesplitres ( argv , argc ) ;
continue ;
}
2019-02-06 16:44:55 +01:00
/* The line should start with the "user" keyword. */
2019-02-07 12:04:25 +01:00
if ( strcmp ( argv [ 0 ] , " user " ) | | argc < 2 ) {
2019-02-06 16:44:55 +01:00
errors = sdscatprintf ( errors ,
2019-02-07 17:07:35 +01:00
" %s:%d should start with user keyword followed "
" by the username. " , server . acl_filename ,
2019-02-06 16:44:55 +01:00
linenum ) ;
2019-02-07 12:04:25 +01:00
sdsfreesplitres ( argv , argc ) ;
2019-02-06 16:44:55 +01:00
continue ;
}
2020-04-15 16:39:42 +02:00
/* Spaces are not allowed in usernames. */
if ( ACLStringHasSpaces ( argv [ 1 ] , sdslen ( argv [ 1 ] ) ) ) {
errors = sdscatprintf ( errors ,
" '%s:%d: username '%s' contains invalid characters. " ,
server . acl_filename , linenum , argv [ 1 ] ) ;
2020-08-08 07:42:32 -04:00
sdsfreesplitres ( argv , argc ) ;
2020-04-15 16:39:42 +02:00
continue ;
}
2021-09-09 07:40:33 -07:00
user * u = ACLCreateUser ( argv [ 1 ] , sdslen ( argv [ 1 ] ) ) ;
/* If the user already exists we assume it's an error and abort. */
if ( ! u ) {
errors = sdscatprintf ( errors , " WARNING: Duplicate user '%s' found on line %d. " , argv [ 1 ] , linenum ) ;
sdsfreesplitres ( argv , argc ) ;
continue ;
}
/* Finally process the options and validate they can
* be cleanly applied to the user . If any option fails
* to apply , the other values won ' t be applied since
* all the pending changes will get dropped . */
2022-01-20 13:05:27 -08:00
int merged_argc ;
sds * acl_args = ACLMergeSelectorArguments ( argv + 2 , argc - 2 , & merged_argc , NULL ) ;
if ( ! acl_args ) {
errors = sdscatprintf ( errors ,
" %s:%d: Unmatched parenthesis in selector definition. " ,
server . acl_filename , linenum ) ;
}
2022-07-09 21:02:22 -07:00
int syntax_error = 0 ;
for ( int j = 0 ; j < merged_argc ; j + + ) {
2022-01-20 13:05:27 -08:00
acl_args [ j ] = sdstrim ( acl_args [ j ] , " \t \r \n " ) ;
if ( ACLSetUser ( u , acl_args [ j ] , sdslen ( acl_args [ j ] ) ) ! = C_OK ) {
2021-01-21 11:56:08 +02:00
const char * errmsg = ACLSetUserStringError ( ) ;
2022-07-09 21:02:22 -07:00
if ( errno = = ENOENT ) {
/* For missing commands, we print out more information since
* it shouldn ' t contain any sensitive information . */
errors = sdscatprintf ( errors ,
" %s:%d: Error in applying operation '%s': %s. " ,
server . acl_filename , linenum , acl_args [ j ] , errmsg ) ;
} else if ( syntax_error = = 0 ) {
/* For all other errors, only print out the first error encountered
* since it might affect future operations . */
errors = sdscatprintf ( errors ,
" %s:%d: %s. " ,
server . acl_filename , linenum , errmsg ) ;
syntax_error = 1 ;
}
2019-02-06 16:44:55 +01:00
}
}
2019-02-07 17:07:35 +01:00
2022-01-20 13:05:27 -08:00
for ( int i = 0 ; i < merged_argc ; i + + ) sdsfree ( acl_args [ i ] ) ;
zfree ( acl_args ) ;
2019-02-07 17:07:35 +01:00
/* Apply the rule to the new users set only if so far there
* are no errors , otherwise it ' s useless since we are going
* to discard the new users set anyway . */
if ( sdslen ( errors ) ! = 0 ) {
2019-02-07 12:04:25 +01:00
sdsfreesplitres ( argv , argc ) ;
2019-02-07 17:07:35 +01:00
continue ;
2019-02-07 12:04:25 +01:00
}
sdsfreesplitres ( argv , argc ) ;
2019-02-06 12:39:11 +01:00
}
sdsfreesplitres ( lines , totlines ) ;
2019-02-07 12:57:21 +01:00
2019-02-07 16:47:14 +01:00
/* Check if we found errors and react accordingly. */
2019-02-06 12:39:11 +01:00
if ( sdslen ( errors ) = = 0 ) {
2019-02-07 12:57:21 +01:00
/* The default user pointer is referenced in different places: instead
* of replacing such occurrences it is much simpler to copy the new
* default user configuration in the old one . */
2021-09-09 07:40:33 -07:00
user * new_default = ACLGetUserByName ( " default " , 7 ) ;
if ( ! new_default ) {
new_default = ACLCreateDefaultUser ( ) ;
}
ACLCopyUser ( DefaultUser , new_default ) ;
ACLFreeUser ( new_default ) ;
2019-02-07 12:57:21 +01:00
raxInsert ( Users , ( unsigned char * ) " default " , 7 , DefaultUser , NULL ) ;
2019-02-07 16:20:42 +01:00
raxRemove ( old_users , ( unsigned char * ) " default " , 7 , NULL ) ;
2023-12-24 11:56:44 +02:00
/* If there are some subscribers, we need to check if we need to drop some clients. */
rax * user_channels = NULL ;
Refactor the per-slot dict-array db.c into a new kvstore data structure (#12822)
# Description
Gather most of the scattered `redisDb`-related code from the per-slot
dict PR (#11695) and turn it to a new data structure, `kvstore`. i.e.
it's a class that represents an array of dictionaries.
# Motivation
The main motivation is code cleanliness, the idea of using an array of
dictionaries is very well-suited to becoming a self-contained data
structure.
This allowed cleaning some ugly code, among others: loops that run twice
on the main dict and expires dict, and duplicate code for allocating and
releasing this data structure.
# Notes
1. This PR reverts the part of https://github.com/redis/redis/pull/12848
where the `rehashing` list is global (handling rehashing `dict`s is
under the responsibility of `kvstore`, and should not be managed by the
server)
2. This PR also replaces the type of `server.pubsubshard_channels` from
`dict**` to `kvstore` (original PR:
https://github.com/redis/redis/pull/12804). After that was done,
server.pubsub_channels was also chosen to be a `kvstore` (with only one
`dict`, which seems odd) just to make the code cleaner by making it the
same type as `server.pubsubshard_channels`, see
`pubsubtype.serverPubSubChannels`
3. the keys and expires kvstores are currenlty configured to allocate
the individual dicts only when the first key is added (unlike before, in
which they allocated them in advance), but they won't release them when
the last key is deleted.
Worth mentioning that due to the recent change the reply of DEBUG
HTSTATS changed, in case no keys were ever added to the db.
before:
```
127.0.0.1:6379> DEBUG htstats 9
[Dictionary HT]
Hash table 0 stats (main hash table):
No stats available for empty dictionaries
[Expires HT]
Hash table 0 stats (main hash table):
No stats available for empty dictionaries
```
after:
```
127.0.0.1:6379> DEBUG htstats 9
[Dictionary HT]
[Expires HT]
```
2024-02-05 22:21:35 +07:00
if ( pubsubTotalSubscriptions ( ) > 0 ) {
2023-12-24 11:56:44 +02:00
user_channels = raxNew ( ) ;
}
listIter li ;
listNode * ln ;
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
client * c = listNodeValue ( ln ) ;
user * original = c - > user ;
list * channels = NULL ;
user * new = ACLGetUserByName ( c - > user - > name , sdslen ( c - > user - > name ) ) ;
if ( new & & user_channels ) {
if ( ! raxFind ( user_channels , ( unsigned char * ) ( new - > name ) , sdslen ( new - > name ) , ( void * * ) & channels ) ) {
channels = getUpcomingChannelList ( new , original ) ;
raxInsert ( user_channels , ( unsigned char * ) ( new - > name ) , sdslen ( new - > name ) , channels , NULL ) ;
}
}
/* When the new channel list is NULL, it means the new user's channel list is a superset of the old user's list. */
if ( ! new | | ( channels & & ACLShouldKillPubsubClient ( c , channels ) ) ) {
freeClient ( c ) ;
continue ;
}
c - > user = new ;
}
if ( user_channels )
raxFreeWithCallback ( user_channels , ( void ( * ) ( void * ) ) listRelease ) ;
raxFreeWithCallback ( old_users , ( void ( * ) ( void * ) ) ACLFreeUser ) ;
2019-02-06 12:39:11 +01:00
sdsfree ( errors ) ;
2019-02-06 16:44:55 +01:00
return NULL ;
} else {
2023-12-24 11:56:44 +02:00
raxFreeWithCallback ( Users , ( void ( * ) ( void * ) ) ACLFreeUser ) ;
2019-02-07 12:57:21 +01:00
Users = old_users ;
errors = sdscat ( errors , " WARNING: ACL errors detected, no change to the previously active ACL rules was performed " ) ;
return errors ;
2019-02-06 12:39:11 +01:00
}
}
2019-02-21 16:50:28 +01:00
/* Generate a copy of the ACLs currently in memory in the specified filename.
* Returns C_OK on success or C_ERR if there was an error during the I / O .
* When C_ERR is returned a log is produced with hints about the issue . */
int ACLSaveToFile ( const char * filename ) {
sds acl = sdsempty ( ) ;
2019-02-22 12:41:57 +01:00
int fd = - 1 ;
sds tmpfilename = NULL ;
int retval = C_ERR ;
2019-02-21 16:50:28 +01:00
/* Let's generate an SDS string containing the new version of the
* ACL file . */
raxIterator ri ;
raxStart ( & ri , Users ) ;
raxSeek ( & ri , " ^ " , NULL , 0 ) ;
while ( raxNext ( & ri ) ) {
user * u = ri . data ;
/* Return information in the configuration file format. */
sds user = sdsnew ( " user " ) ;
user = sdscatsds ( user , u - > name ) ;
user = sdscatlen ( user , " " , 1 ) ;
2022-09-22 16:29:00 +03:00
robj * descr = ACLDescribeUser ( u ) ;
user = sdscatsds ( user , descr - > ptr ) ;
decrRefCount ( descr ) ;
2019-02-21 16:50:28 +01:00
acl = sdscatsds ( acl , user ) ;
acl = sdscatlen ( acl , " \n " , 1 ) ;
sdsfree ( user ) ;
}
raxStop ( & ri ) ;
/* Create a temp file with the new content. */
2019-02-22 12:41:57 +01:00
tmpfilename = sdsnew ( filename ) ;
2019-02-21 16:50:28 +01:00
tmpfilename = sdscatfmt ( tmpfilename , " .tmp-%i-%I " ,
2022-10-09 13:18:34 +08:00
( int ) getpid ( ) , commandTimeSnapshot ( ) ) ;
2019-02-21 16:50:28 +01:00
if ( ( fd = open ( tmpfilename , O_WRONLY | O_CREAT , 0644 ) ) = = - 1 ) {
serverLog ( LL_WARNING , " Opening temp ACL file for ACL SAVE: %s " ,
strerror ( errno ) ) ;
2019-02-22 12:41:57 +01:00
goto cleanup ;
2019-02-21 16:50:28 +01:00
}
/* Write it. */
2022-07-20 14:11:01 +08:00
size_t offset = 0 ;
while ( offset < sdslen ( acl ) ) {
ssize_t written_bytes = write ( fd , acl + offset , sdslen ( acl ) - offset ) ;
if ( written_bytes < = 0 ) {
if ( errno = = EINTR ) continue ;
serverLog ( LL_WARNING , " Writing ACL file for ACL SAVE: %s " ,
strerror ( errno ) ) ;
goto cleanup ;
}
offset + = written_bytes ;
}
2024-04-23 20:20:35 +08:00
if ( valkey_fsync ( fd ) = = - 1 ) {
2022-07-20 14:11:01 +08:00
serverLog ( LL_WARNING , " Syncing ACL file for ACL SAVE: %s " ,
2019-02-21 16:50:28 +01:00
strerror ( errno ) ) ;
2019-02-22 12:41:57 +01:00
goto cleanup ;
2019-02-21 16:50:28 +01:00
}
2019-02-22 12:41:57 +01:00
close ( fd ) ; fd = - 1 ;
2019-02-21 16:50:28 +01:00
/* Let's replace the new file with the old one. */
if ( rename ( tmpfilename , filename ) = = - 1 ) {
serverLog ( LL_WARNING , " Renaming ACL file for ACL SAVE: %s " ,
strerror ( errno ) ) ;
2019-02-22 12:41:57 +01:00
goto cleanup ;
2019-02-21 16:50:28 +01:00
}
2022-07-20 14:11:01 +08:00
if ( fsyncFileDir ( filename ) = = - 1 ) {
serverLog ( LL_WARNING , " Syncing ACL directory for ACL SAVE: %s " ,
strerror ( errno ) ) ;
goto cleanup ;
}
2019-02-22 12:41:57 +01:00
sdsfree ( tmpfilename ) ; tmpfilename = NULL ;
retval = C_OK ; /* If we reached this point, everything is fine. */
2019-02-21 16:50:28 +01:00
2019-02-22 12:41:57 +01:00
cleanup :
if ( fd ! = - 1 ) close ( fd ) ;
if ( tmpfilename ) unlink ( tmpfilename ) ;
2019-02-21 16:50:28 +01:00
sdsfree ( tmpfilename ) ;
2019-02-22 12:41:57 +01:00
sdsfree ( acl ) ;
2019-02-22 12:45:13 +01:00
return retval ;
2019-02-21 16:50:28 +01:00
}
2019-02-07 17:20:03 +01:00
/* This function is called once the server is already running, modules are
* loaded , and we are ready to start , in order to load the ACLs either from
2024-04-03 10:47:26 -07:00
* the pending list of users defined in valkey . conf , or from the ACL file .
2019-02-07 17:20:03 +01:00
* The function will just exit with an error if the user is trying to mix
* both the loading methods . */
void ACLLoadUsersAtStartup ( void ) {
if ( server . acl_filename [ 0 ] ! = ' \0 ' & & listLength ( UsersToLoad ) ! = 0 ) {
serverLog ( LL_WARNING ,
2024-04-17 19:38:21 +07:00
" Configuring %s with users defined in valkey.conf and at "
2019-02-07 17:20:03 +01:00
" the same setting an ACL file path is invalid. This setup "
" is very likely to lead to configuration errors and security "
" holes, please define either an ACL file or declare users "
2024-04-17 19:38:21 +07:00
" directly in your valkey.conf, but not both. " , SERVER_TITLE ) ;
2019-02-07 17:20:03 +01:00
exit ( 1 ) ;
}
if ( ACLLoadConfiguredUsers ( ) = = C_ERR ) {
serverLog ( LL_WARNING ,
" Critical error while loading ACLs. Exiting. " ) ;
exit ( 1 ) ;
}
if ( server . acl_filename [ 0 ] ! = ' \0 ' ) {
sds errors = ACLLoadFromFile ( server . acl_filename ) ;
if ( errors ) {
serverLog ( LL_WARNING ,
2024-04-17 19:38:21 +07:00
" Aborting %s startup because of ACL errors: %s " , SERVER_TITLE , errors ) ;
2019-02-07 17:20:03 +01:00
sdsfree ( errors ) ;
exit ( 1 ) ;
}
}
}
2020-01-27 18:37:52 +01:00
/* =============================================================================
* ACL log
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2020-01-29 18:40:32 +01:00
# define ACL_LOG_GROUPING_MAX_TIME_DELTA 60000
2020-01-27 18:37:52 +01:00
/* This structure defines an entry inside the ACL log. */
2020-01-28 18:04:20 +01:00
typedef struct ACLLogEntry {
2020-01-27 18:37:52 +01:00
uint64_t count ; /* Number of times this happened recently. */
int reason ; /* Reason for denying the command. ACL_DENIED_*. */
int context ; /* Toplevel, Lua or MULTI/EXEC? ACL_LOG_CTX_*. */
sds object ; /* The key name or command name. */
sds username ; /* User the client is authenticated with. */
mstime_t ctime ; /* Milliseconds time of last update to this entry. */
sds cinfo ; /* Client info (last client if updated). */
2023-02-02 12:12:16 -08:00
long long entry_id ; /* The pair (entry_id, timestamp_created) is a unique identifier of this entry
* in case the node dies and is restarted , it can detect that if it ' s a new series . */
mstime_t timestamp_created ; /* UNIX time in milliseconds at the time of this entry's creation. */
2020-01-28 18:04:20 +01:00
} ACLLogEntry ;
2020-01-27 18:37:52 +01:00
2020-01-29 18:40:32 +01:00
/* This function will check if ACL entries 'a' and 'b' are similar enough
* that we should actually update the existing entry in our ACL log instead
* of creating a new one . */
int ACLLogMatchEntry ( ACLLogEntry * a , ACLLogEntry * b ) {
if ( a - > reason ! = b - > reason ) return 0 ;
if ( a - > context ! = b - > context ) return 0 ;
mstime_t delta = a - > ctime - b - > ctime ;
if ( delta < 0 ) delta = - delta ;
if ( delta > ACL_LOG_GROUPING_MAX_TIME_DELTA ) return 0 ;
if ( sdscmp ( a - > object , b - > object ) ! = 0 ) return 0 ;
if ( sdscmp ( a - > username , b - > username ) ! = 0 ) return 0 ;
return 1 ;
}
/* Release an ACL log entry. */
2020-01-29 18:51:04 +01:00
void ACLFreeLogEntry ( void * leptr ) {
ACLLogEntry * le = leptr ;
2020-01-29 18:40:32 +01:00
sdsfree ( le - > object ) ;
sdsfree ( le - > username ) ;
sdsfree ( le - > cinfo ) ;
zfree ( le ) ;
}
2022-10-07 20:19:34 +03:00
/* Update the relevant counter by the reason */
void ACLUpdateInfoMetrics ( int reason ) {
if ( reason = = ACL_DENIED_AUTH ) {
server . acl_info . user_auth_failures + + ;
} else if ( reason = = ACL_DENIED_CMD ) {
server . acl_info . invalid_cmd_accesses + + ;
} else if ( reason = = ACL_DENIED_KEY ) {
server . acl_info . invalid_key_accesses + + ;
} else if ( reason = = ACL_DENIED_CHANNEL ) {
server . acl_info . invalid_channel_accesses + + ;
} else {
serverPanic ( " Unknown ACL_DENIED encoding " ) ;
}
}
2024-01-22 16:01:04 -08:00
static void trimACLLogEntriesToMaxLen ( void ) {
while ( listLength ( ACLLog ) > server . acllog_max_len ) {
listNode * ln = listLast ( ACLLog ) ;
ACLLogEntry * le = listNodeValue ( ln ) ;
ACLFreeLogEntry ( le ) ;
listDelNode ( ACLLog , ln ) ;
}
}
2020-01-28 17:30:50 +01:00
/* Adds a new entry in the ACL log, making sure to delete the old entry
* if we reach the maximum length allowed for the log . This function attempts
* to find similar entries in the current log in order to bump the counter of
* the log entry instead of creating many entries for very similar ACL
2020-02-04 12:55:26 +01:00
* rules issues .
*
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
* The argpos argument is used when the reason is ACL_DENIED_KEY or
* ACL_DENIED_CHANNEL , since it allows the function to log the key or channel
2021-09-23 08:52:56 +03:00
* name that caused the problem .
*
* The last 2 arguments are a manual override to be used , instead of any of the automatic
* ones which depend on the client and reason arguments ( use NULL for default ) .
Auto-generate the command table from JSON files (#9656)
Delete the hardcoded command table and replace it with an auto-generated table, based
on a JSON file that describes the commands (each command must have a JSON file).
These JSON files are the SSOT of everything there is to know about Redis commands,
and it is reflected fully in COMMAND INFO.
These JSON files are used to generate commands.c (using a python script), which is then
committed to the repo and compiled.
The purpose is:
* Clients and proxies will be able to get much more info from redis, instead of relying on hard coded logic.
* drop the dependency between Redis-user and the commands.json in redis-doc.
* delete help.h and have redis-cli learn everything it needs to know just by issuing COMMAND (will be
done in a separate PR)
* redis.io should stop using commands.json and learn everything from Redis (ultimately one of the release
artifacts should be a large JSON, containing all the information about all of the commands, which will be
generated from COMMAND's reply)
* the byproduct of this is:
* module commands will be able to provide that info and possibly be more of a first-class citizens
* in theory, one may be able to generate a redis client library for a strictly typed language, by using this info.
### Interface changes
#### COMMAND INFO's reply change (and arg-less COMMAND)
Before this commit the reply at index 7 contained the key-specs list
and reply at index 8 contained the sub-commands list (Both unreleased).
Now, reply at index 7 is a map of:
- summary - short command description
- since - debut version
- group - command group
- complexity - complexity string
- doc-flags - flags used for documentation (e.g. "deprecated")
- deprecated-since - if deprecated, from which version?
- replaced-by - if deprecated, which command replaced it?
- history - a list of (version, what-changed) tuples
- hints - a list of strings, meant to provide hints for clients/proxies. see https://github.com/redis/redis/issues/9876
- arguments - an array of arguments. each element is a map, with the possibility of nesting (sub-arguments)
- key-specs - an array of keys specs (already in unstable, just changed location)
- subcommands - a list of sub-commands (already in unstable, just changed location)
- reply-schema - will be added in the future (see https://github.com/redis/redis/issues/9845)
more details on these can be found in https://github.com/redis/redis-doc/pull/1697
only the first three fields are mandatory
#### API changes (unreleased API obviously)
now they take RedisModuleCommand opaque pointer instead of looking up the command by name
- RM_CreateSubcommand
- RM_AddCommandKeySpec
- RM_SetCommandKeySpecBeginSearchIndex
- RM_SetCommandKeySpecBeginSearchKeyword
- RM_SetCommandKeySpecFindKeysRange
- RM_SetCommandKeySpecFindKeysKeynum
Currently, we did not add module API to provide additional information about their commands because
we couldn't agree on how the API should look like, see https://github.com/redis/redis/issues/9944.
### Somehow related changes
1. Literals should be in uppercase while placeholder in lowercase. Now all the GEO* command
will be documented with M|KM|FT|MI and can take both lowercase and uppercase
### Unrelated changes
1. Bugfix: no_madaory_keys was absent in COMMAND's reply
2. expose CMD_MODULE as "module" via COMMAND
3. have a dedicated uint64 for ACL categories (instead of having them in the same uint64 as command flags)
Co-authored-by: Itamar Haber <itamar@garantiadata.com>
2021-12-15 20:23:15 +01:00
*
* If ` object ` is not NULL , this functions takes over it .
2020-02-04 12:55:26 +01:00
*/
2021-09-23 08:52:56 +03:00
void addACLLogEntry ( client * c , int reason , int context , int argpos , sds username , sds object ) {
2022-10-07 20:19:34 +03:00
/* Update ACL info metrics */
ACLUpdateInfoMetrics ( reason ) ;
2024-01-22 16:01:04 -08:00
if ( server . acllog_max_len = = 0 ) {
trimACLLogEntriesToMaxLen ( ) ;
return ;
}
2020-01-27 18:37:52 +01:00
/* Create a new entry. */
2020-01-28 18:04:20 +01:00
struct ACLLogEntry * le = zmalloc ( sizeof ( * le ) ) ;
2020-01-27 18:37:52 +01:00
le - > count = 1 ;
2020-01-28 18:04:20 +01:00
le - > reason = reason ;
2021-09-23 08:52:56 +03:00
le - > username = sdsdup ( username ? username : c - > user - > name ) ;
2022-10-09 13:18:34 +08:00
le - > ctime = commandTimeSnapshot ( ) ;
2023-02-02 12:12:16 -08:00
le - > entry_id = ACLLogEntryCount ;
le - > timestamp_created = le - > ctime ;
2020-01-27 18:37:52 +01:00
2021-09-23 08:52:56 +03:00
if ( object ) {
Auto-generate the command table from JSON files (#9656)
Delete the hardcoded command table and replace it with an auto-generated table, based
on a JSON file that describes the commands (each command must have a JSON file).
These JSON files are the SSOT of everything there is to know about Redis commands,
and it is reflected fully in COMMAND INFO.
These JSON files are used to generate commands.c (using a python script), which is then
committed to the repo and compiled.
The purpose is:
* Clients and proxies will be able to get much more info from redis, instead of relying on hard coded logic.
* drop the dependency between Redis-user and the commands.json in redis-doc.
* delete help.h and have redis-cli learn everything it needs to know just by issuing COMMAND (will be
done in a separate PR)
* redis.io should stop using commands.json and learn everything from Redis (ultimately one of the release
artifacts should be a large JSON, containing all the information about all of the commands, which will be
generated from COMMAND's reply)
* the byproduct of this is:
* module commands will be able to provide that info and possibly be more of a first-class citizens
* in theory, one may be able to generate a redis client library for a strictly typed language, by using this info.
### Interface changes
#### COMMAND INFO's reply change (and arg-less COMMAND)
Before this commit the reply at index 7 contained the key-specs list
and reply at index 8 contained the sub-commands list (Both unreleased).
Now, reply at index 7 is a map of:
- summary - short command description
- since - debut version
- group - command group
- complexity - complexity string
- doc-flags - flags used for documentation (e.g. "deprecated")
- deprecated-since - if deprecated, from which version?
- replaced-by - if deprecated, which command replaced it?
- history - a list of (version, what-changed) tuples
- hints - a list of strings, meant to provide hints for clients/proxies. see https://github.com/redis/redis/issues/9876
- arguments - an array of arguments. each element is a map, with the possibility of nesting (sub-arguments)
- key-specs - an array of keys specs (already in unstable, just changed location)
- subcommands - a list of sub-commands (already in unstable, just changed location)
- reply-schema - will be added in the future (see https://github.com/redis/redis/issues/9845)
more details on these can be found in https://github.com/redis/redis-doc/pull/1697
only the first three fields are mandatory
#### API changes (unreleased API obviously)
now they take RedisModuleCommand opaque pointer instead of looking up the command by name
- RM_CreateSubcommand
- RM_AddCommandKeySpec
- RM_SetCommandKeySpecBeginSearchIndex
- RM_SetCommandKeySpecBeginSearchKeyword
- RM_SetCommandKeySpecFindKeysRange
- RM_SetCommandKeySpecFindKeysKeynum
Currently, we did not add module API to provide additional information about their commands because
we couldn't agree on how the API should look like, see https://github.com/redis/redis/issues/9944.
### Somehow related changes
1. Literals should be in uppercase while placeholder in lowercase. Now all the GEO* command
will be documented with M|KM|FT|MI and can take both lowercase and uppercase
### Unrelated changes
1. Bugfix: no_madaory_keys was absent in COMMAND's reply
2. expose CMD_MODULE as "module" via COMMAND
3. have a dedicated uint64 for ACL categories (instead of having them in the same uint64 as command flags)
Co-authored-by: Itamar Haber <itamar@garantiadata.com>
2021-12-15 20:23:15 +01:00
le - > object = object ;
2021-09-23 08:52:56 +03:00
} else {
2024-05-08 20:58:53 +02:00
/* clang-format off */
2021-09-23 08:52:56 +03:00
switch ( reason ) {
2024-05-08 20:58:53 +02:00
case ACL_DENIED_CMD : le - > object = sdsdup ( c - > cmd - > fullname ) ; break ;
case ACL_DENIED_KEY : le - > object = sdsdup ( c - > argv [ argpos ] - > ptr ) ; break ;
case ACL_DENIED_CHANNEL : le - > object = sdsdup ( c - > argv [ argpos ] - > ptr ) ; break ;
case ACL_DENIED_AUTH : le - > object = sdsdup ( c - > argv [ 0 ] - > ptr ) ; break ;
default : le - > object = sdsempty ( ) ;
2021-09-23 08:52:56 +03:00
}
2024-05-08 20:58:53 +02:00
/* clang-format on */
2020-02-04 12:55:26 +01:00
}
2023-02-16 08:07:35 +02:00
/* if we have a real client from the network, use it (could be missing on module timers) */
client * realclient = server . current_client ? server . current_client : c ;
2020-01-27 18:37:52 +01:00
le - > cinfo = catClientInfoString ( sdsempty ( ) , realclient ) ;
2021-09-23 08:52:56 +03:00
le - > context = context ;
2020-01-27 18:37:52 +01:00
2020-01-29 18:40:32 +01:00
/* Try to match this entry with past ones, to see if we can just
* update an existing entry instead of creating a new one . */
long toscan = 10 ; /* Do a limited work trying to find duplicated. */
listIter li ;
listNode * ln ;
listRewind ( ACLLog , & li ) ;
ACLLogEntry * match = NULL ;
while ( toscan - - & & ( ln = listNext ( & li ) ) ! = NULL ) {
ACLLogEntry * current = listNodeValue ( ln ) ;
if ( ACLLogMatchEntry ( current , le ) ) {
match = current ;
listDelNode ( ACLLog , ln ) ;
listAddNodeHead ( ACLLog , current ) ;
break ;
}
}
/* If there is a match update the entry, otherwise add it as a
* new one . */
if ( match ) {
/* We update a few fields of the existing entry and bump the
* counter of events for this entry . */
sdsfree ( match - > cinfo ) ;
match - > cinfo = le - > cinfo ;
match - > ctime = le - > ctime ;
match - > count + + ;
/* Release the old entry. */
le - > cinfo = NULL ;
ACLFreeLogEntry ( le ) ;
} else {
2020-11-08 14:32:38 +08:00
/* Add it to our list of entries. We'll have to trim the list
2020-01-29 18:40:32 +01:00
* to its maximum size . */
2023-02-02 12:12:16 -08:00
ACLLogEntryCount + + ; /* Incrementing the entry_id count to make each record in the log unique. */
2020-01-29 18:40:32 +01:00
listAddNodeHead ( ACLLog , le ) ;
2024-01-22 16:01:04 -08:00
trimACLLogEntriesToMaxLen ( ) ;
2020-01-29 18:40:32 +01:00
}
2020-01-27 18:37:52 +01:00
}
2024-04-03 23:54:33 +07:00
sds getAclErrorMessage ( int acl_res , user * user , struct serverCommand * cmd , sds errored_val , int verbose ) {
Add new RM_Call flags for script mode, no writes, and error replies. (#10372)
The PR extends RM_Call with 3 new capabilities using new flags that
are given to RM_Call as part of the `fmt` argument.
It aims to assist modules that are getting a list of commands to be
executed from the user (not hard coded as part of the module logic),
think of a module that implements a new scripting language...
* `S` - Run the command in a script mode, this means that it will raise an
error if a command which are not allowed inside a script (flaged with the
`deny-script` flag) is invoked (like SHUTDOWN). In addition, on script mode,
write commands are not allowed if there is not enough good replicas (as
configured with `min-replicas-to-write`) and/or a disk error happened.
* `W` - no writes mode, Redis will reject any command that is marked with `write`
flag. Again can be useful to modules that implement a new scripting language
and wants to prevent any write commands.
* `E` - Return errors as RedisModuleCallReply. Today the errors that happened
before the command was invoked (like unknown commands or acl error) return
a NULL reply and set errno. This might be missing important information about
the failure and it is also impossible to just pass the error to the user using
RM_ReplyWithCallReply. This new flag allows you to get a RedisModuleCallReply
object with the relevant error message and treat it as if it was an error that was
raised by the command invocation.
Tests were added to verify the new code paths.
In addition small refactoring was done to share some code between modules,
scripts, and `processCommand` function:
1. `getAclErrorMessage` was added to `acl.c` to unified to log message extraction
from the acl result
2. `checkGoodReplicasStatus` was added to `replication.c` to check the status of
good replicas. It is used on `scriptVerifyWriteCommandAllow`, `RM_Call`, and
`processCommand`.
3. `writeCommandsGetDiskErrorMessage` was added to `server.c` to get the error
message on persistence failure. Again it is used on `scriptVerifyWriteCommandAllow`,
`RM_Call`, and `processCommand`.
2022-03-22 14:13:28 +02:00
switch ( acl_res ) {
case ACL_DENIED_CMD :
Unify ACL failure error messaging. (#11160)
Motivation: for applications that use RM ACL verification functions, they would
want to return errors back to the user, in ways that are consistent with Redis.
While investigating how we should return ACL errors to the user, we realized that
Redis isn't consistent, and currently returns ACL error strings in 3 primary ways.
[For the actual implications of this change, see the "Impact" section at the bottom]
1. how it returns an error when calling a command normally
ACL_DENIED_CMD -> "this user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "this user has no permissions to access one of the keys used as arguments"
ACL_DENIED_CHANNEL -> "this user has no permissions to access one of the channels used as arguments"
2. how it returns an error when calling via 'acl dryrun' command
ACL_DENIED_CMD -> "This user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "This user has no permissions to access the '%s' key"
ACL_DENIED_CHANNEL -> "This user has no permissions to access the '%s' channel"
3. how it returns an error via RM_Call (and scripting is similar).
ACL_DENIED_CMD -> "can't run this command or subcommand";
ACL_DENIED_KEY -> "can't access at least one of the keys mentioned in the command arguments";
ACL_DENIED_CHANNEL -> "can't publish to the channel mentioned in the command";
In addition, if one wants to use RM_Call's "dry run" capability instead of the RM ACL
functions directly, one also sees a different problem than it returns ACL errors with a -ERR,
not a -PERM, so it can't be returned directly to the caller.
This PR modifies the code to generate a base message in a common manner with the ability
to set verbose flag for acl dry run errors, and keep it unset for normal/rm_call/script cases
```c
sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose) {
switch (acl_res) {
case ACL_DENIED_CMD:
return sdscatfmt(sdsempty(), "User %S has no permissions to run "
"the '%S' command", user->name, cmd->fullname);
case ACL_DENIED_KEY:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' key", user->name, errored_val);
} else {
return sdsnew("No permissions to access a key");
}
case ACL_DENIED_CHANNEL:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' channel", user->name, errored_val);
} else {
return sdsnew("No permissions to access a channel");
}
}
```
The caller can append/prepend the message (adding NOPERM for normal/RM_Call or indicating it's within a script).
Impact:
- Plain commands, as well as scripts and RM_Call now include the user name.
- ACL DRYRUN remains the only one that's verbose (mentions the offending channel or key name)
- Changes RM_Call ACL errors from being a `-ERR` to being `-NOPERM` (besides for textual changes)
**This somewhat a breaking change, but it only affects the RM_Call with both `C` and `E`, or `D`**
- Changes ACL errors in scripts textually from being
`The user executing the script <old non unified text>`
to
`ACL failure in script: <new unified text>`
2022-10-16 09:01:37 +03:00
return sdscatfmt ( sdsempty ( ) , " User %S has no permissions to run "
" the '%S' command " , user - > name , cmd - > fullname ) ;
Add new RM_Call flags for script mode, no writes, and error replies. (#10372)
The PR extends RM_Call with 3 new capabilities using new flags that
are given to RM_Call as part of the `fmt` argument.
It aims to assist modules that are getting a list of commands to be
executed from the user (not hard coded as part of the module logic),
think of a module that implements a new scripting language...
* `S` - Run the command in a script mode, this means that it will raise an
error if a command which are not allowed inside a script (flaged with the
`deny-script` flag) is invoked (like SHUTDOWN). In addition, on script mode,
write commands are not allowed if there is not enough good replicas (as
configured with `min-replicas-to-write`) and/or a disk error happened.
* `W` - no writes mode, Redis will reject any command that is marked with `write`
flag. Again can be useful to modules that implement a new scripting language
and wants to prevent any write commands.
* `E` - Return errors as RedisModuleCallReply. Today the errors that happened
before the command was invoked (like unknown commands or acl error) return
a NULL reply and set errno. This might be missing important information about
the failure and it is also impossible to just pass the error to the user using
RM_ReplyWithCallReply. This new flag allows you to get a RedisModuleCallReply
object with the relevant error message and treat it as if it was an error that was
raised by the command invocation.
Tests were added to verify the new code paths.
In addition small refactoring was done to share some code between modules,
scripts, and `processCommand` function:
1. `getAclErrorMessage` was added to `acl.c` to unified to log message extraction
from the acl result
2. `checkGoodReplicasStatus` was added to `replication.c` to check the status of
good replicas. It is used on `scriptVerifyWriteCommandAllow`, `RM_Call`, and
`processCommand`.
3. `writeCommandsGetDiskErrorMessage` was added to `server.c` to get the error
message on persistence failure. Again it is used on `scriptVerifyWriteCommandAllow`,
`RM_Call`, and `processCommand`.
2022-03-22 14:13:28 +02:00
case ACL_DENIED_KEY :
Unify ACL failure error messaging. (#11160)
Motivation: for applications that use RM ACL verification functions, they would
want to return errors back to the user, in ways that are consistent with Redis.
While investigating how we should return ACL errors to the user, we realized that
Redis isn't consistent, and currently returns ACL error strings in 3 primary ways.
[For the actual implications of this change, see the "Impact" section at the bottom]
1. how it returns an error when calling a command normally
ACL_DENIED_CMD -> "this user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "this user has no permissions to access one of the keys used as arguments"
ACL_DENIED_CHANNEL -> "this user has no permissions to access one of the channels used as arguments"
2. how it returns an error when calling via 'acl dryrun' command
ACL_DENIED_CMD -> "This user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "This user has no permissions to access the '%s' key"
ACL_DENIED_CHANNEL -> "This user has no permissions to access the '%s' channel"
3. how it returns an error via RM_Call (and scripting is similar).
ACL_DENIED_CMD -> "can't run this command or subcommand";
ACL_DENIED_KEY -> "can't access at least one of the keys mentioned in the command arguments";
ACL_DENIED_CHANNEL -> "can't publish to the channel mentioned in the command";
In addition, if one wants to use RM_Call's "dry run" capability instead of the RM ACL
functions directly, one also sees a different problem than it returns ACL errors with a -ERR,
not a -PERM, so it can't be returned directly to the caller.
This PR modifies the code to generate a base message in a common manner with the ability
to set verbose flag for acl dry run errors, and keep it unset for normal/rm_call/script cases
```c
sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose) {
switch (acl_res) {
case ACL_DENIED_CMD:
return sdscatfmt(sdsempty(), "User %S has no permissions to run "
"the '%S' command", user->name, cmd->fullname);
case ACL_DENIED_KEY:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' key", user->name, errored_val);
} else {
return sdsnew("No permissions to access a key");
}
case ACL_DENIED_CHANNEL:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' channel", user->name, errored_val);
} else {
return sdsnew("No permissions to access a channel");
}
}
```
The caller can append/prepend the message (adding NOPERM for normal/RM_Call or indicating it's within a script).
Impact:
- Plain commands, as well as scripts and RM_Call now include the user name.
- ACL DRYRUN remains the only one that's verbose (mentions the offending channel or key name)
- Changes RM_Call ACL errors from being a `-ERR` to being `-NOPERM` (besides for textual changes)
**This somewhat a breaking change, but it only affects the RM_Call with both `C` and `E`, or `D`**
- Changes ACL errors in scripts textually from being
`The user executing the script <old non unified text>`
to
`ACL failure in script: <new unified text>`
2022-10-16 09:01:37 +03:00
if ( verbose ) {
return sdscatfmt ( sdsempty ( ) , " User %S has no permissions to access "
" the '%S' key " , user - > name , errored_val ) ;
} else {
return sdsnew ( " No permissions to access a key " ) ;
}
Add new RM_Call flags for script mode, no writes, and error replies. (#10372)
The PR extends RM_Call with 3 new capabilities using new flags that
are given to RM_Call as part of the `fmt` argument.
It aims to assist modules that are getting a list of commands to be
executed from the user (not hard coded as part of the module logic),
think of a module that implements a new scripting language...
* `S` - Run the command in a script mode, this means that it will raise an
error if a command which are not allowed inside a script (flaged with the
`deny-script` flag) is invoked (like SHUTDOWN). In addition, on script mode,
write commands are not allowed if there is not enough good replicas (as
configured with `min-replicas-to-write`) and/or a disk error happened.
* `W` - no writes mode, Redis will reject any command that is marked with `write`
flag. Again can be useful to modules that implement a new scripting language
and wants to prevent any write commands.
* `E` - Return errors as RedisModuleCallReply. Today the errors that happened
before the command was invoked (like unknown commands or acl error) return
a NULL reply and set errno. This might be missing important information about
the failure and it is also impossible to just pass the error to the user using
RM_ReplyWithCallReply. This new flag allows you to get a RedisModuleCallReply
object with the relevant error message and treat it as if it was an error that was
raised by the command invocation.
Tests were added to verify the new code paths.
In addition small refactoring was done to share some code between modules,
scripts, and `processCommand` function:
1. `getAclErrorMessage` was added to `acl.c` to unified to log message extraction
from the acl result
2. `checkGoodReplicasStatus` was added to `replication.c` to check the status of
good replicas. It is used on `scriptVerifyWriteCommandAllow`, `RM_Call`, and
`processCommand`.
3. `writeCommandsGetDiskErrorMessage` was added to `server.c` to get the error
message on persistence failure. Again it is used on `scriptVerifyWriteCommandAllow`,
`RM_Call`, and `processCommand`.
2022-03-22 14:13:28 +02:00
case ACL_DENIED_CHANNEL :
Unify ACL failure error messaging. (#11160)
Motivation: for applications that use RM ACL verification functions, they would
want to return errors back to the user, in ways that are consistent with Redis.
While investigating how we should return ACL errors to the user, we realized that
Redis isn't consistent, and currently returns ACL error strings in 3 primary ways.
[For the actual implications of this change, see the "Impact" section at the bottom]
1. how it returns an error when calling a command normally
ACL_DENIED_CMD -> "this user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "this user has no permissions to access one of the keys used as arguments"
ACL_DENIED_CHANNEL -> "this user has no permissions to access one of the channels used as arguments"
2. how it returns an error when calling via 'acl dryrun' command
ACL_DENIED_CMD -> "This user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "This user has no permissions to access the '%s' key"
ACL_DENIED_CHANNEL -> "This user has no permissions to access the '%s' channel"
3. how it returns an error via RM_Call (and scripting is similar).
ACL_DENIED_CMD -> "can't run this command or subcommand";
ACL_DENIED_KEY -> "can't access at least one of the keys mentioned in the command arguments";
ACL_DENIED_CHANNEL -> "can't publish to the channel mentioned in the command";
In addition, if one wants to use RM_Call's "dry run" capability instead of the RM ACL
functions directly, one also sees a different problem than it returns ACL errors with a -ERR,
not a -PERM, so it can't be returned directly to the caller.
This PR modifies the code to generate a base message in a common manner with the ability
to set verbose flag for acl dry run errors, and keep it unset for normal/rm_call/script cases
```c
sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose) {
switch (acl_res) {
case ACL_DENIED_CMD:
return sdscatfmt(sdsempty(), "User %S has no permissions to run "
"the '%S' command", user->name, cmd->fullname);
case ACL_DENIED_KEY:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' key", user->name, errored_val);
} else {
return sdsnew("No permissions to access a key");
}
case ACL_DENIED_CHANNEL:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' channel", user->name, errored_val);
} else {
return sdsnew("No permissions to access a channel");
}
}
```
The caller can append/prepend the message (adding NOPERM for normal/RM_Call or indicating it's within a script).
Impact:
- Plain commands, as well as scripts and RM_Call now include the user name.
- ACL DRYRUN remains the only one that's verbose (mentions the offending channel or key name)
- Changes RM_Call ACL errors from being a `-ERR` to being `-NOPERM` (besides for textual changes)
**This somewhat a breaking change, but it only affects the RM_Call with both `C` and `E`, or `D`**
- Changes ACL errors in scripts textually from being
`The user executing the script <old non unified text>`
to
`ACL failure in script: <new unified text>`
2022-10-16 09:01:37 +03:00
if ( verbose ) {
return sdscatfmt ( sdsempty ( ) , " User %S has no permissions to access "
" the '%S' channel " , user - > name , errored_val ) ;
} else {
return sdsnew ( " No permissions to access a channel " ) ;
}
Add new RM_Call flags for script mode, no writes, and error replies. (#10372)
The PR extends RM_Call with 3 new capabilities using new flags that
are given to RM_Call as part of the `fmt` argument.
It aims to assist modules that are getting a list of commands to be
executed from the user (not hard coded as part of the module logic),
think of a module that implements a new scripting language...
* `S` - Run the command in a script mode, this means that it will raise an
error if a command which are not allowed inside a script (flaged with the
`deny-script` flag) is invoked (like SHUTDOWN). In addition, on script mode,
write commands are not allowed if there is not enough good replicas (as
configured with `min-replicas-to-write`) and/or a disk error happened.
* `W` - no writes mode, Redis will reject any command that is marked with `write`
flag. Again can be useful to modules that implement a new scripting language
and wants to prevent any write commands.
* `E` - Return errors as RedisModuleCallReply. Today the errors that happened
before the command was invoked (like unknown commands or acl error) return
a NULL reply and set errno. This might be missing important information about
the failure and it is also impossible to just pass the error to the user using
RM_ReplyWithCallReply. This new flag allows you to get a RedisModuleCallReply
object with the relevant error message and treat it as if it was an error that was
raised by the command invocation.
Tests were added to verify the new code paths.
In addition small refactoring was done to share some code between modules,
scripts, and `processCommand` function:
1. `getAclErrorMessage` was added to `acl.c` to unified to log message extraction
from the acl result
2. `checkGoodReplicasStatus` was added to `replication.c` to check the status of
good replicas. It is used on `scriptVerifyWriteCommandAllow`, `RM_Call`, and
`processCommand`.
3. `writeCommandsGetDiskErrorMessage` was added to `server.c` to get the error
message on persistence failure. Again it is used on `scriptVerifyWriteCommandAllow`,
`RM_Call`, and `processCommand`.
2022-03-22 14:13:28 +02:00
}
serverPanic ( " Reached deadcode on getAclErrorMessage " ) ;
}
2019-01-10 16:35:55 +01:00
/* =============================================================================
* ACL related commands
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2019-01-15 09:36:12 +01:00
2022-01-23 16:05:06 +08:00
/* ACL CAT category */
void aclCatWithFlags ( client * c , dict * commands , uint64_t cflag , int * arraylen ) {
dictEntry * de ;
dictIterator * di = dictGetIterator ( commands ) ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd = dictGetVal ( de ) ;
2022-01-23 16:05:06 +08:00
if ( cmd - > flags & CMD_MODULE ) continue ;
if ( cmd - > acl_categories & cflag ) {
addReplyBulkCBuffer ( c , cmd - > fullname , sdslen ( cmd - > fullname ) ) ;
( * arraylen ) + + ;
}
if ( cmd - > subcommands_dict ) {
aclCatWithFlags ( c , cmd - > subcommands_dict , cflag , arraylen ) ;
}
}
dictReleaseIterator ( di ) ;
}
2022-01-20 13:05:27 -08:00
/* Add the formatted response from a single selector to the ACL GETUSER
* response . This function returns the number of fields added .
*
* Setting verbose to 1 means that the full qualifier for key and channel
* permissions are shown .
*/
int aclAddReplySelectorDescription ( client * c , aclSelector * s ) {
listIter li ;
listNode * ln ;
/* Commands */
addReplyBulkCString ( c , " commands " ) ;
sds cmddescr = ACLDescribeSelectorCommandRules ( s ) ;
addReplyBulkSds ( c , cmddescr ) ;
/* Key patterns */
addReplyBulkCString ( c , " keys " ) ;
if ( s - > flags & SELECTOR_FLAG_ALLKEYS ) {
addReplyBulkCBuffer ( c , " ~* " , 2 ) ;
} else {
sds dsl = sdsempty ( ) ;
listRewind ( s - > patterns , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
keyPattern * thispat = ( keyPattern * ) listNodeValue ( ln ) ;
if ( ln ! = listFirst ( s - > patterns ) ) dsl = sdscat ( dsl , " " ) ;
dsl = sdsCatPatternString ( dsl , thispat ) ;
}
addReplyBulkSds ( c , dsl ) ;
}
/* Pub/sub patterns */
addReplyBulkCString ( c , " channels " ) ;
if ( s - > flags & SELECTOR_FLAG_ALLCHANNELS ) {
addReplyBulkCBuffer ( c , " &* " , 2 ) ;
} else {
sds dsl = sdsempty ( ) ;
listRewind ( s - > channels , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
sds thispat = listNodeValue ( ln ) ;
if ( ln ! = listFirst ( s - > channels ) ) dsl = sdscat ( dsl , " " ) ;
dsl = sdscatfmt ( dsl , " &%S " , thispat ) ;
}
addReplyBulkSds ( c , dsl ) ;
}
return 3 ;
}
2019-01-15 09:36:12 +01:00
/* ACL -- show and modify the configuration of ACL users.
2019-01-15 18:16:20 +01:00
* ACL HELP
2019-02-21 16:31:33 +01:00
* ACL LOAD
2020-02-06 10:31:43 +02:00
* ACL SAVE
2019-01-15 18:16:20 +01:00
* ACL LIST
2019-02-08 12:40:42 +01:00
* ACL USERS
* ACL CAT [ < category > ]
* ACL SETUSER < username > . . . acl rules . . .
2019-02-21 00:34:54 +00:00
* ACL DELUSER < username > [ . . . ]
2019-01-15 18:16:20 +01:00
* ACL GETUSER < username >
2020-04-23 10:53:21 +02:00
* ACL GENPASS [ < bits > ]
2019-03-05 15:51:37 +01:00
* ACL WHOAMI
2020-01-28 17:30:50 +01:00
* ACL LOG [ < count > | RESET ]
2019-01-15 09:36:12 +01:00
*/
void aclCommand ( client * c ) {
char * sub = c - > argv [ 1 ] - > ptr ;
if ( ! strcasecmp ( sub , " setuser " ) & & c - > argc > = 3 ) {
2021-12-13 08:39:04 -08:00
/* Initially redact all of the arguments to not leak any information
* about the user . */
for ( int j = 2 ; j < c - > argc ; j + + ) {
redactClientCommandArgument ( c , j ) ;
}
2019-01-15 09:36:12 +01:00
sds username = c - > argv [ 2 ] - > ptr ;
2020-04-15 16:39:42 +02:00
/* Check username validity. */
if ( ACLStringHasSpaces ( username , sdslen ( username ) ) ) {
2023-11-30 18:36:17 +08:00
addReplyError ( c , " Usernames can't contain spaces or null characters " ) ;
2020-04-15 16:39:42 +02:00
return ;
}
2022-09-22 16:29:00 +03:00
user * u = ACLGetUserByName ( username , sdslen ( username ) ) ;
2022-01-29 20:43:16 +08:00
sds * temp_argv = zmalloc ( c - > argc * sizeof ( sds ) ) ;
2022-01-20 13:05:27 -08:00
for ( int i = 3 ; i < c - > argc ; i + + ) temp_argv [ i - 3 ] = c - > argv [ i ] - > ptr ;
2022-09-22 16:29:00 +03:00
sds error = ACLStringSetUser ( u , username , temp_argv , c - > argc - 3 ) ;
zfree ( temp_argv ) ;
if ( error = = NULL ) {
addReply ( c , shared . ok ) ;
} else {
addReplyErrorSdsSafe ( c , error ) ;
2019-01-15 09:36:12 +01:00
}
2022-01-20 13:05:27 -08:00
return ;
2019-01-31 18:33:14 +01:00
} else if ( ! strcasecmp ( sub , " deluser " ) & & c - > argc > = 3 ) {
2023-12-13 21:28:13 +08:00
/* Initially redact all the arguments to not leak any information
* about the users . */
for ( int j = 2 ; j < c - > argc ; j + + ) redactClientCommandArgument ( c , j ) ;
2019-01-31 18:33:14 +01:00
int deleted = 0 ;
for ( int j = 2 ; j < c - > argc ; j + + ) {
sds username = c - > argv [ j ] - > ptr ;
if ( ! strcmp ( username , " default " ) ) {
addReplyError ( c , " The 'default' user cannot be removed " ) ;
return ;
}
2019-02-21 00:34:54 +00:00
}
for ( int j = 2 ; j < c - > argc ; j + + ) {
sds username = c - > argv [ j ] - > ptr ;
2019-01-31 18:33:14 +01:00
user * u ;
if ( raxRemove ( Users , ( unsigned char * ) username ,
sdslen ( username ) ,
( void * * ) & u ) )
{
2019-02-11 22:24:15 +08:00
ACLFreeUserAndKillClients ( u ) ;
2019-01-31 18:33:14 +01:00
deleted + + ;
}
}
addReplyLongLong ( c , deleted ) ;
2019-01-17 18:19:04 +01:00
} else if ( ! strcasecmp ( sub , " getuser " ) & & c - > argc = = 3 ) {
2023-12-13 21:28:13 +08:00
/* Redact the username to not leak any information about the user. */
redactClientCommandArgument ( c , 2 ) ;
2019-01-17 18:19:04 +01:00
user * u = ACLGetUserByName ( c - > argv [ 2 ] - > ptr , sdslen ( c - > argv [ 2 ] - > ptr ) ) ;
2019-01-23 23:57:18 +08:00
if ( u = = NULL ) {
addReplyNull ( c ) ;
return ;
}
2022-01-20 13:05:27 -08:00
void * ufields = addReplyDeferredLen ( c ) ;
int fields = 3 ;
2019-01-17 18:19:04 +01:00
/* Flags */
addReplyBulkCString ( c , " flags " ) ;
void * deflen = addReplyDeferredLen ( c ) ;
int numflags = 0 ;
2019-01-31 16:49:22 +01:00
for ( int j = 0 ; ACLUserFlags [ j ] . flag ; j + + ) {
if ( u - > flags & ACLUserFlags [ j ] . flag ) {
addReplyBulkCString ( c , ACLUserFlags [ j ] . name ) ;
numflags + + ;
}
2019-01-17 18:19:04 +01:00
}
setDeferredSetLen ( c , deflen , numflags ) ;
/* Passwords */
addReplyBulkCString ( c , " passwords " ) ;
addReplyArrayLen ( c , listLength ( u - > passwords ) ) ;
listIter li ;
listNode * ln ;
listRewind ( u - > passwords , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
sds thispass = listNodeValue ( ln ) ;
addReplyBulkCBuffer ( c , thispass , sdslen ( thispass ) ) ;
}
2022-01-20 13:05:27 -08:00
/* Include the root selector at the top level for backwards compatibility */
fields + = aclAddReplySelectorDescription ( c , ACLUserGetRootSelector ( u ) ) ;
2019-01-29 18:54:21 +01:00
2022-01-20 13:05:27 -08:00
/* Describe all of the selectors on this user, including duplicating the root selector */
addReplyBulkCString ( c , " selectors " ) ;
addReplyArrayLen ( c , listLength ( u - > selectors ) - 1 ) ;
listRewind ( u - > selectors , & li ) ;
serverAssert ( listNext ( & li ) ) ;
while ( ( ln = listNext ( & li ) ) ) {
void * slen = addReplyDeferredLen ( c ) ;
int sfields = aclAddReplySelectorDescription ( c , ( aclSelector * ) listNodeValue ( ln ) ) ;
setDeferredMapLen ( c , slen , sfields ) ;
}
setDeferredMapLen ( c , ufields , fields ) ;
2019-01-31 18:32:49 +01:00
} else if ( ( ! strcasecmp ( sub , " list " ) | | ! strcasecmp ( sub , " users " ) ) & &
c - > argc = = 2 )
{
2019-01-31 17:01:28 +01:00
int justnames = ! strcasecmp ( sub , " users " ) ;
addReplyArrayLen ( c , raxSize ( Users ) ) ;
raxIterator ri ;
raxStart ( & ri , Users ) ;
raxSeek ( & ri , " ^ " , NULL , 0 ) ;
while ( raxNext ( & ri ) ) {
user * u = ri . data ;
if ( justnames ) {
addReplyBulkCBuffer ( c , u - > name , sdslen ( u - > name ) ) ;
} else {
/* Return information in the configuration file format. */
sds config = sdsnew ( " user " ) ;
config = sdscatsds ( config , u - > name ) ;
config = sdscatlen ( config , " " , 1 ) ;
2022-09-22 16:29:00 +03:00
robj * descr = ACLDescribeUser ( u ) ;
config = sdscatsds ( config , descr - > ptr ) ;
decrRefCount ( descr ) ;
2019-01-31 17:01:28 +01:00
addReplyBulkSds ( c , config ) ;
}
}
raxStop ( & ri ) ;
2019-02-08 12:38:41 +01:00
} else if ( ! strcasecmp ( sub , " whoami " ) & & c - > argc = = 2 ) {
2019-01-31 17:01:28 +01:00
if ( c - > user ! = NULL ) {
addReplyBulkCBuffer ( c , c - > user - > name , sdslen ( c - > user - > name ) ) ;
} else {
addReplyNull ( c ) ;
}
2019-02-21 17:01:08 +01:00
} else if ( server . acl_filename [ 0 ] = = ' \0 ' & &
( ! strcasecmp ( sub , " load " ) | | ! strcasecmp ( sub , " save " ) ) )
{
2024-04-16 21:17:38 +02:00
addReplyError ( c , " This instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a configuration file set) in order to store users in the configuration. " ) ;
2019-02-21 17:01:08 +01:00
return ;
2019-02-08 12:38:41 +01:00
} else if ( ! strcasecmp ( sub , " load " ) & & c - > argc = = 2 ) {
2019-02-21 17:01:08 +01:00
sds errors = ACLLoadFromFile ( server . acl_filename ) ;
if ( errors = = NULL ) {
addReply ( c , shared . ok ) ;
2019-02-07 12:20:30 +01:00
} else {
2019-02-21 17:01:08 +01:00
addReplyError ( c , errors ) ;
sdsfree ( errors ) ;
}
} else if ( ! strcasecmp ( sub , " save " ) & & c - > argc = = 2 ) {
if ( ACLSaveToFile ( server . acl_filename ) = = C_OK ) {
addReply ( c , shared . ok ) ;
} else {
addReplyError ( c , " There was an error trying to save the ACLs. "
" Please check the server logs for more "
" information " ) ;
2019-02-07 12:20:30 +01:00
}
2019-02-12 17:02:45 +01:00
} else if ( ! strcasecmp ( sub , " cat " ) & & c - > argc = = 2 ) {
void * dl = addReplyDeferredLen ( c ) ;
int j ;
for ( j = 0 ; ACLCommandCategories [ j ] . flag ! = 0 ; j + + )
addReplyBulkCString ( c , ACLCommandCategories [ j ] . name ) ;
setDeferredArrayLen ( c , dl , j ) ;
} else if ( ! strcasecmp ( sub , " cat " ) & & c - > argc = = 3 ) {
uint64_t cflag = ACLGetCommandCategoryFlagByName ( c - > argv [ 2 ] - > ptr ) ;
if ( cflag = = 0 ) {
2022-01-23 16:05:06 +08:00
addReplyErrorFormat ( c , " Unknown category '%.128s' " , ( char * ) c - > argv [ 2 ] - > ptr ) ;
2019-02-12 17:02:45 +01:00
return ;
}
int arraylen = 0 ;
void * dl = addReplyDeferredLen ( c ) ;
2022-01-23 16:05:06 +08:00
aclCatWithFlags ( c , server . orig_commands , cflag , & arraylen ) ;
2019-02-12 17:02:45 +01:00
setDeferredArrayLen ( c , dl , arraylen ) ;
2020-04-23 10:53:21 +02:00
} else if ( ! strcasecmp ( sub , " genpass " ) & & ( c - > argc = = 2 | | c - > argc = = 3 ) ) {
# define GENPASS_MAX_BITS 4096
char pass [ GENPASS_MAX_BITS / 8 * 2 ] ; /* Hex representation. */
long bits = 256 ; /* By default generate 256 bits passwords. */
if ( c - > argc = = 3 & & getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & bits , NULL )
! = C_OK ) return ;
if ( bits < = 0 | | bits > GENPASS_MAX_BITS ) {
addReplyErrorFormat ( c ,
" ACL GENPASS argument must be the number of "
" bits for the output password, a positive number "
" up to %d " , GENPASS_MAX_BITS ) ;
return ;
}
long chars = ( bits + 3 ) / 4 ; /* Round to number of characters to emit. */
getRandomHexChars ( pass , chars ) ;
addReplyBulkCBuffer ( c , pass , chars ) ;
2020-01-28 17:30:50 +01:00
} else if ( ! strcasecmp ( sub , " log " ) & & ( c - > argc = = 2 | | c - > argc = = 3 ) ) {
long count = 10 ; /* Number of entries to emit by default. */
/* Parse the only argument that LOG may have: it could be either
2020-04-14 00:16:29 -04:00
* the number of entries the user wants to display , or alternatively
* the " RESET " command in order to flush the old entries . */
2020-01-28 17:30:50 +01:00
if ( c - > argc = = 3 ) {
if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " reset " ) ) {
2020-01-29 18:51:04 +01:00
listSetFreeMethod ( ACLLog , ACLFreeLogEntry ) ;
listEmpty ( ACLLog ) ;
listSetFreeMethod ( ACLLog , NULL ) ;
2020-01-28 17:30:50 +01:00
addReply ( c , shared . ok ) ;
return ;
} else if ( getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & count , NULL )
! = C_OK )
{
return ;
}
if ( count < 0 ) count = 0 ;
}
/* Fix the count according to the number of entries we got. */
if ( ( size_t ) count > listLength ( ACLLog ) )
count = listLength ( ACLLog ) ;
addReplyArrayLen ( c , count ) ;
listIter li ;
listNode * ln ;
listRewind ( ACLLog , & li ) ;
2022-10-09 13:18:34 +08:00
mstime_t now = commandTimeSnapshot ( ) ;
2020-01-28 17:30:50 +01:00
while ( count - - & & ( ln = listNext ( & li ) ) ! = NULL ) {
2020-01-28 18:04:20 +01:00
ACLLogEntry * le = listNodeValue ( ln ) ;
2023-02-02 12:12:16 -08:00
addReplyMapLen ( c , 10 ) ;
2020-01-28 18:04:20 +01:00
addReplyBulkCString ( c , " count " ) ;
addReplyLongLong ( c , le - > count ) ;
2020-02-04 12:55:26 +01:00
2020-01-28 18:04:20 +01:00
addReplyBulkCString ( c , " reason " ) ;
2020-02-04 12:55:26 +01:00
char * reasonstr ;
2024-05-08 20:58:53 +02:00
/* clang-format off */
2020-02-04 12:55:26 +01:00
switch ( le - > reason ) {
case ACL_DENIED_CMD : reasonstr = " command " ; break ;
case ACL_DENIED_KEY : reasonstr = " key " ; break ;
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.
This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.
The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.
Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
literally exist(s) in the user's list.
Such failures are logged to the ACL log.
Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.
Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
for touching them.
2020-12-01 14:21:39 +02:00
case ACL_DENIED_CHANNEL : reasonstr = " channel " ; break ;
2020-02-04 12:55:26 +01:00
case ACL_DENIED_AUTH : reasonstr = " auth " ; break ;
2020-02-29 18:28:41 +08:00
default : reasonstr = " unknown " ;
2020-02-04 12:55:26 +01:00
}
2024-05-08 20:58:53 +02:00
/* clang-format on */
2020-02-04 12:55:26 +01:00
addReplyBulkCString ( c , reasonstr ) ;
addReplyBulkCString ( c , " context " ) ;
2020-01-28 18:04:20 +01:00
char * ctxstr ;
2024-05-08 20:58:53 +02:00
/* clang-format off */
2020-01-28 18:04:20 +01:00
switch ( le - > context ) {
case ACL_LOG_CTX_TOPLEVEL : ctxstr = " toplevel " ; break ;
case ACL_LOG_CTX_MULTI : ctxstr = " multi " ; break ;
case ACL_LOG_CTX_LUA : ctxstr = " lua " ; break ;
2021-09-23 08:52:56 +03:00
case ACL_LOG_CTX_MODULE : ctxstr = " module " ; break ;
2020-01-28 18:04:20 +01:00
default : ctxstr = " unknown " ;
}
2024-05-08 20:58:53 +02:00
/* clang-format on */
2020-01-28 18:04:20 +01:00
addReplyBulkCString ( c , ctxstr ) ;
2020-02-04 12:55:26 +01:00
2020-01-28 18:04:20 +01:00
addReplyBulkCString ( c , " object " ) ;
addReplyBulkCBuffer ( c , le - > object , sdslen ( le - > object ) ) ;
addReplyBulkCString ( c , " username " ) ;
addReplyBulkCBuffer ( c , le - > username , sdslen ( le - > username ) ) ;
addReplyBulkCString ( c , " age-seconds " ) ;
double age = ( double ) ( now - le - > ctime ) / 1000 ;
addReplyDouble ( c , age ) ;
addReplyBulkCString ( c , " client-info " ) ;
addReplyBulkCBuffer ( c , le - > cinfo , sdslen ( le - > cinfo ) ) ;
2023-02-02 12:12:16 -08:00
addReplyBulkCString ( c , " entry-id " ) ;
addReplyLongLong ( c , le - > entry_id ) ;
addReplyBulkCString ( c , " timestamp-created " ) ;
addReplyLongLong ( c , le - > timestamp_created ) ;
addReplyBulkCString ( c , " timestamp-last-updated " ) ;
addReplyLongLong ( c , le - > ctime ) ;
2020-01-28 17:30:50 +01:00
}
2022-01-20 13:05:27 -08:00
} else if ( ! strcasecmp ( sub , " dryrun " ) & & c - > argc > = 4 ) {
2024-04-03 23:54:33 +07:00
struct serverCommand * cmd ;
2022-01-20 13:05:27 -08:00
user * u = ACLGetUserByName ( c - > argv [ 2 ] - > ptr , sdslen ( c - > argv [ 2 ] - > ptr ) ) ;
if ( u = = NULL ) {
addReplyErrorFormat ( c , " User '%s' not found " , ( char * ) c - > argv [ 2 ] - > ptr ) ;
return ;
}
if ( ( cmd = lookupCommand ( c - > argv + 3 , c - > argc - 3 ) ) = = NULL ) {
addReplyErrorFormat ( c , " Command '%s' not found " , ( char * ) c - > argv [ 3 ] - > ptr ) ;
return ;
}
2022-03-10 10:08:41 +02:00
if ( ( cmd - > arity > 0 & & cmd - > arity ! = c - > argc - 3 ) | |
( c - > argc - 3 < - cmd - > arity ) )
{
addReplyErrorFormat ( c , " wrong number of arguments for '%s' command " , cmd - > fullname ) ;
return ;
}
2022-01-20 13:05:27 -08:00
int idx ;
int result = ACLCheckAllUserCommandPerm ( u , cmd , c - > argv + 3 , c - > argc - 3 , & idx ) ;
if ( result ! = ACL_OK ) {
Unify ACL failure error messaging. (#11160)
Motivation: for applications that use RM ACL verification functions, they would
want to return errors back to the user, in ways that are consistent with Redis.
While investigating how we should return ACL errors to the user, we realized that
Redis isn't consistent, and currently returns ACL error strings in 3 primary ways.
[For the actual implications of this change, see the "Impact" section at the bottom]
1. how it returns an error when calling a command normally
ACL_DENIED_CMD -> "this user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "this user has no permissions to access one of the keys used as arguments"
ACL_DENIED_CHANNEL -> "this user has no permissions to access one of the channels used as arguments"
2. how it returns an error when calling via 'acl dryrun' command
ACL_DENIED_CMD -> "This user has no permissions to run the '%s' command"
ACL_DENIED_KEY -> "This user has no permissions to access the '%s' key"
ACL_DENIED_CHANNEL -> "This user has no permissions to access the '%s' channel"
3. how it returns an error via RM_Call (and scripting is similar).
ACL_DENIED_CMD -> "can't run this command or subcommand";
ACL_DENIED_KEY -> "can't access at least one of the keys mentioned in the command arguments";
ACL_DENIED_CHANNEL -> "can't publish to the channel mentioned in the command";
In addition, if one wants to use RM_Call's "dry run" capability instead of the RM ACL
functions directly, one also sees a different problem than it returns ACL errors with a -ERR,
not a -PERM, so it can't be returned directly to the caller.
This PR modifies the code to generate a base message in a common manner with the ability
to set verbose flag for acl dry run errors, and keep it unset for normal/rm_call/script cases
```c
sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose) {
switch (acl_res) {
case ACL_DENIED_CMD:
return sdscatfmt(sdsempty(), "User %S has no permissions to run "
"the '%S' command", user->name, cmd->fullname);
case ACL_DENIED_KEY:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' key", user->name, errored_val);
} else {
return sdsnew("No permissions to access a key");
}
case ACL_DENIED_CHANNEL:
if (verbose) {
return sdscatfmt(sdsempty(), "User %S has no permissions to access "
"the '%S' channel", user->name, errored_val);
} else {
return sdsnew("No permissions to access a channel");
}
}
```
The caller can append/prepend the message (adding NOPERM for normal/RM_Call or indicating it's within a script).
Impact:
- Plain commands, as well as scripts and RM_Call now include the user name.
- ACL DRYRUN remains the only one that's verbose (mentions the offending channel or key name)
- Changes RM_Call ACL errors from being a `-ERR` to being `-NOPERM` (besides for textual changes)
**This somewhat a breaking change, but it only affects the RM_Call with both `C` and `E`, or `D`**
- Changes ACL errors in scripts textually from being
`The user executing the script <old non unified text>`
to
`ACL failure in script: <new unified text>`
2022-10-16 09:01:37 +03:00
sds err = getAclErrorMessage ( result , u , cmd , c - > argv [ idx + 3 ] - > ptr , 1 ) ;
2022-01-20 13:05:27 -08:00
addReplyBulkSds ( c , err ) ;
return ;
}
addReply ( c , shared . ok ) ;
2020-07-15 17:38:22 +08:00
} else if ( c - > argc = = 2 & & ! strcasecmp ( sub , " help " ) ) {
2024-05-08 20:58:53 +02:00
/* clang-format off */
2019-01-15 09:36:12 +01:00
const char * help [ ] = {
2021-01-04 17:02:57 +02:00
" CAT [<category>] " ,
" List all commands that belong to <category>, or all command categories " ,
" when no category is specified. " ,
" DELUSER <username> [<username> ...] " ,
" Delete a list of users. " ,
2022-01-20 13:05:27 -08:00
" DRYRUN <username> <command> [<arg> ...] " ,
" Returns whether the user can execute the given command without executing the command. " ,
2021-01-04 17:02:57 +02:00
" GETUSER <username> " ,
" Get the user's details. " ,
" GENPASS [<bits>] " ,
" Generate a secure 256-bit user password. The optional `bits` argument can " ,
" be used to specify a different size. " ,
" LIST " ,
" Show users details in config file format. " ,
" LOAD " ,
" Reload users from the ACL file. " ,
" LOG [<count> | RESET] " ,
" Show the ACL log entries. " ,
" SAVE " ,
" Save the current config to the ACL file. " ,
" SETUSER <username> <attribute> [<attribute> ...] " ,
" Create or modify a user with the specified attributes. " ,
" USERS " ,
" List all the registered usernames. " ,
" WHOAMI " ,
" Return the current connection username. " ,
2019-01-15 09:36:12 +01:00
NULL
} ;
2024-05-08 20:58:53 +02:00
/* clang-format on */
2019-01-15 09:36:12 +01:00
addReplyHelp ( c , help ) ;
} else {
addReplySubcommandSyntaxError ( c ) ;
}
}
2019-02-14 00:12:10 +08:00
2024-04-03 23:54:33 +07:00
void addReplyCommandCategories ( client * c , struct serverCommand * cmd ) {
2019-02-14 00:12:10 +08:00
int flagcount = 0 ;
void * flaglen = addReplyDeferredLen ( c ) ;
for ( int j = 0 ; ACLCommandCategories [ j ] . flag ! = 0 ; j + + ) {
Auto-generate the command table from JSON files (#9656)
Delete the hardcoded command table and replace it with an auto-generated table, based
on a JSON file that describes the commands (each command must have a JSON file).
These JSON files are the SSOT of everything there is to know about Redis commands,
and it is reflected fully in COMMAND INFO.
These JSON files are used to generate commands.c (using a python script), which is then
committed to the repo and compiled.
The purpose is:
* Clients and proxies will be able to get much more info from redis, instead of relying on hard coded logic.
* drop the dependency between Redis-user and the commands.json in redis-doc.
* delete help.h and have redis-cli learn everything it needs to know just by issuing COMMAND (will be
done in a separate PR)
* redis.io should stop using commands.json and learn everything from Redis (ultimately one of the release
artifacts should be a large JSON, containing all the information about all of the commands, which will be
generated from COMMAND's reply)
* the byproduct of this is:
* module commands will be able to provide that info and possibly be more of a first-class citizens
* in theory, one may be able to generate a redis client library for a strictly typed language, by using this info.
### Interface changes
#### COMMAND INFO's reply change (and arg-less COMMAND)
Before this commit the reply at index 7 contained the key-specs list
and reply at index 8 contained the sub-commands list (Both unreleased).
Now, reply at index 7 is a map of:
- summary - short command description
- since - debut version
- group - command group
- complexity - complexity string
- doc-flags - flags used for documentation (e.g. "deprecated")
- deprecated-since - if deprecated, from which version?
- replaced-by - if deprecated, which command replaced it?
- history - a list of (version, what-changed) tuples
- hints - a list of strings, meant to provide hints for clients/proxies. see https://github.com/redis/redis/issues/9876
- arguments - an array of arguments. each element is a map, with the possibility of nesting (sub-arguments)
- key-specs - an array of keys specs (already in unstable, just changed location)
- subcommands - a list of sub-commands (already in unstable, just changed location)
- reply-schema - will be added in the future (see https://github.com/redis/redis/issues/9845)
more details on these can be found in https://github.com/redis/redis-doc/pull/1697
only the first three fields are mandatory
#### API changes (unreleased API obviously)
now they take RedisModuleCommand opaque pointer instead of looking up the command by name
- RM_CreateSubcommand
- RM_AddCommandKeySpec
- RM_SetCommandKeySpecBeginSearchIndex
- RM_SetCommandKeySpecBeginSearchKeyword
- RM_SetCommandKeySpecFindKeysRange
- RM_SetCommandKeySpecFindKeysKeynum
Currently, we did not add module API to provide additional information about their commands because
we couldn't agree on how the API should look like, see https://github.com/redis/redis/issues/9944.
### Somehow related changes
1. Literals should be in uppercase while placeholder in lowercase. Now all the GEO* command
will be documented with M|KM|FT|MI and can take both lowercase and uppercase
### Unrelated changes
1. Bugfix: no_madaory_keys was absent in COMMAND's reply
2. expose CMD_MODULE as "module" via COMMAND
3. have a dedicated uint64 for ACL categories (instead of having them in the same uint64 as command flags)
Co-authored-by: Itamar Haber <itamar@garantiadata.com>
2021-12-15 20:23:15 +01:00
if ( cmd - > acl_categories & ACLCommandCategories [ j ] . flag ) {
2019-02-14 00:12:10 +08:00
addReplyStatusFormat ( c , " @%s " , ACLCommandCategories [ j ] . name ) ;
flagcount + + ;
}
}
setDeferredSetLen ( c , flaglen , flagcount ) ;
}
2019-02-25 16:33:36 +01:00
2019-06-07 13:20:22 -07:00
/* AUTH <password>
2024-04-09 01:24:03 -07:00
* AUTH < username > < password > ( Redis OSS > = 6.0 form )
2019-02-25 16:33:36 +01:00
*
* When the user is omitted it means that we are trying to authenticate
* against the default user . */
void authCommand ( client * c ) {
/* Only two or three argument forms are allowed. */
if ( c - > argc > 3 ) {
2020-12-23 19:06:25 -08:00
addReplyErrorObject ( c , shared . syntaxerr ) ;
2019-02-25 16:33:36 +01:00
return ;
}
2021-05-19 08:23:54 -07:00
/* Always redact the second argument */
redactClientCommandArgument ( c , 1 ) ;
2019-02-25 16:33:36 +01:00
/* Handle the two different forms here. The form with two arguments
* will just use " default " as username . */
robj * username , * password ;
if ( c - > argc = = 2 ) {
2021-06-10 20:39:33 +08:00
/* Mimic the old behavior of giving an error for the two argument
* form if no password is configured . */
2019-02-25 16:33:36 +01:00
if ( DefaultUser - > flags & USER_FLAG_NOPASS ) {
addReplyError ( c , " AUTH <password> called without any password "
" configured for the default user. Are you sure "
" your configuration is correct? " ) ;
return ;
}
2021-02-09 11:52:28 -08:00
username = shared . default_username ;
2019-02-25 16:33:36 +01:00
password = c - > argv [ 1 ] ;
} else {
username = c - > argv [ 1 ] ;
password = c - > argv [ 2 ] ;
2021-05-19 08:23:54 -07:00
redactClientCommandArgument ( c , 2 ) ;
2019-02-25 16:33:36 +01:00
}
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
robj * err = NULL ;
int result = ACLAuthenticateUser ( c , username , password , & err ) ;
if ( result = = AUTH_OK ) {
addReply ( c , shared . ok ) ;
} else if ( result = = AUTH_ERR ) {
addAuthErrReply ( c , err ) ;
2019-02-25 16:33:36 +01:00
}
Custom authentication for Modules (#11659)
This change adds new module callbacks that can override the default password based authentication associated with ACLs. With this, Modules can register auth callbacks through which they can implement their own Authentication logic. When `AUTH` and `HELLO AUTH ...` commands are used, Module based authentication is attempted and then normal password based authentication is attempted if needed.
The new Module APIs added in this PR are - `RM_RegisterCustomAuthCallback` and `RM_BlockClientOnAuth` and `RedisModule_ACLAddLogEntryByUserName `.
Module based authentication will be attempted for all Redis users (created through the ACL SETUSER cmd or through Module APIs) even if the Redis user does not exist at the time of the command. This gives a chance for the Module to create the RedisModule user and then authenticate via the RedisModule API - from the custom auth callback.
For the AUTH command, we will support both variations - `AUTH <username> <password>` and `AUTH <password>`. In case of the `AUTH <password>` variation, the custom auth callbacks are triggered with “default” as the username and password as what is provided.
### RedisModule_RegisterCustomAuthCallback
```
void RM_RegisterCustomAuthCallback(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback cb) {
```
This API registers a callback to execute to prior to normal password based authentication. Multiple callbacks can be registered across different modules. These callbacks are responsible for either handling the authentication, each authenticating the user or explicitly denying, or deferring it to other authentication mechanisms. Callbacks are triggered in the order they were registered. When a Module is unloaded, all the auth callbacks registered by it are unregistered. The callbacks are attempted, in the order of most recently registered callbacks, when the AUTH/HELLO (with AUTH field is provided) commands are called. The callbacks will be called with a module context along with a username and a password, and are expected to take one of the following actions:
(1) Authenticate - Use the RM_Authenticate* API successfully and return `REDISMODULE_AUTH_HANDLED`. This will immediately end the auth chain as successful and add the OK reply.
(2) Block a client on authentication - Use the `RM_BlockClientOnAuth` API and return `REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the `RM_UnblockClient `API is used which will trigger the auth reply callback (provided earlier through the `RM_BlockClientOnAuth`). In this reply callback, the Module should authenticate, deny or skip handling authentication.
(3) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the client. Optionally, `err` can be set to a custom error message. This will immediately end the auth chain as unsuccessful and add the ERR reply.
(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the client. This will allow the engine to attempt the next custom auth callback.
If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly.
### RedisModule_BlockClientOnAuth
```
RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleCustomAuthCallback reply_callback,
void (*free_privdata)(RedisModuleCtx*,void*))
```
This API can only be used from a Module from the custom auth callback. If a client is not in the middle of custom module based authentication, ERROR is returned. Otherwise, the client is blocked and the `RedisModule_BlockedClient` is returned similar to the `RedisModule_BlockClient` API.
### RedisModule_ACLAddLogEntryByUserName
```
int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason)
```
Adds a new entry in the ACL log with the `username` RedisModuleString provided. This simplifies the Module usage because now, developers do not need to create a Module User just to add an error ACL Log entry. Aside from accepting username (RedisModuleString) instead of a RedisModuleUser, it is the same as the existing `RedisModule_ACLAddLogEntry` API.
### Breaking changes
- HELLO command - Clients can now only set the client name and RESP protocol from the `HELLO` command if they are authenticated. Also, we now finish command arg validation first and return early with a ERR reply if any arg is invalid. This is to avoid mutating the client name / RESP from a command that would have failed on invalid arguments.
### Notable behaviors
- Module unblocking - Now, we will not allow Modules to block the client from inside the context of a reply callback (triggered from the Module unblock flow `moduleHandleBlockedClients`).
---------
Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com>
2023-03-15 15:18:42 -07:00
if ( err ) decrRefCount ( err ) ;
2019-02-25 16:33:36 +01:00
}
2021-02-25 21:00:27 -08:00
/* Set the password for the "default" ACL user. This implements supports for
* requirepass config , so passing in NULL will set the user to be nopass . */
void ACLUpdateDefaultUserPassword ( sds password ) {
ACLSetUser ( DefaultUser , " resetpass " , - 1 ) ;
if ( password ) {
sds aclop = sdscatlen ( sdsnew ( " > " ) , password , sdslen ( password ) ) ;
ACLSetUser ( DefaultUser , aclop , sdslen ( aclop ) ) ;
sdsfree ( aclop ) ;
} else {
ACLSetUser ( DefaultUser , " nopass " , - 1 ) ;
}
}