ACL: WIP: preserve the old config on loading errors.

This commit is contained in:
antirez 2019-02-07 12:57:21 +01:00
parent 0f0240b526
commit 1790be1496

View File

@ -90,6 +90,7 @@ struct ACLUserFlag {
void ACLResetSubcommandsForCommand(user *u, unsigned long id); void ACLResetSubcommandsForCommand(user *u, unsigned long id);
void ACLResetSubcommands(user *u); void ACLResetSubcommands(user *u);
void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub);
/* ============================================================================= /* =============================================================================
* Helper functions for the rest of the ACL implementation * Helper functions for the rest of the ACL implementation
@ -163,6 +164,11 @@ void ACLListFreeSds(void *item) {
sdsfree(item); sdsfree(item);
} }
/* Method to duplicate list elements from ACL users password/ptterns lists. */
void *ACLListDupSds(void *item) {
return sdsdup(item);
}
/* Create a new user with the specified name, store it in the list /* 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 * of users (the Users global radix tree), and returns a reference to
* the structure representing the user. * the structure representing the user.
@ -178,8 +184,10 @@ user *ACLCreateUser(const char *name, size_t namelen) {
u->patterns = listCreate(); u->patterns = listCreate();
listSetMatchMethod(u->passwords,ACLListMatchSds); listSetMatchMethod(u->passwords,ACLListMatchSds);
listSetFreeMethod(u->passwords,ACLListFreeSds); listSetFreeMethod(u->passwords,ACLListFreeSds);
listSetDupMethod(u->passwords,ACLListDupSds);
listSetMatchMethod(u->patterns,ACLListMatchSds); listSetMatchMethod(u->patterns,ACLListMatchSds);
listSetFreeMethod(u->patterns,ACLListFreeSds); listSetFreeMethod(u->patterns,ACLListFreeSds);
listSetDupMethod(u->patterns,ACLListDupSds);
memset(u->allowed_commands,0,sizeof(u->allowed_commands)); memset(u->allowed_commands,0,sizeof(u->allowed_commands));
raxInsert(Users,(unsigned char*)name,namelen,u,NULL); raxInsert(Users,(unsigned char*)name,namelen,u,NULL);
return u; return u;
@ -212,6 +220,38 @@ void ACLFreeUser(user *u) {
zfree(u); zfree(u);
} }
/* 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);
listRelease(dst->patterns);
dst->passwords = listDup(src->passwords);
dst->patterns = listDup(src->patterns);
memcpy(dst->allowed_commands,src->allowed_commands,
sizeof(dst->allowed_commands));
dst->flags = src->flags;
ACLResetSubcommands(dst);
/* Copy the allowed subcommands array of array of SDS strings. */
if (src->allowed_subcommands) {
for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {
if (src->allowed_subcommands[j]) {
for (int i = 0; src->allowed_subcommands[j][i]; i++)
{
ACLAddAllowedSubcommand(dst, j,
src->allowed_subcommands[j][i]);
}
}
}
}
}
/* Free all the users registered in the radix tree 'users' and free the
* radix tree itself. */
void ACLFreeUsersSet(rax *users) {
/* TODO */
}
/* Given a command ID, this function set by reference 'word' and 'bit' /* Given a command ID, this function set by reference 'word' and 'bit'
* so that user->allowed_commands[word] will address the right word * so that user->allowed_commands[word] will address the right word
* where the corresponding bit for the provided ID is stored, and * where the corresponding bit for the provided ID is stored, and
@ -1043,7 +1083,10 @@ int ACLLoadConfiguredUsers(void) {
* to avoid ending with broken rules if the ACL file is invalid for some * 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 * 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 * each user. For every line that will be found broken the function will
* collect an error message. All the valid lines will be correctly processed. * 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.
* *
* At the end of the process, if no errors were found in the whole file then * 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 * NULL is returned. Otherwise an SDS string describing in a single line
@ -1075,6 +1118,14 @@ sds ACLLoadFromFile(const char *filename) {
* to the real user mentioned in the ACL line. */ * to the real user mentioned in the ACL line. */
user *fakeuser = ACLCreateUnlinkedUser(); user *fakeuser = ACLCreateUnlinkedUser();
/* We do all the loading in a fresh insteance of the Users radix tree,
* so if there are errors loading the ACL file we can rollback to the
* old version. */
rax *old_users = Users;
Users = raxNew();
ACLInitDefaultUser();
/* Load each line of the file. */
for (int i = 0; i < totlines; i++) { for (int i = 0; i < totlines; i++) {
sds *argv; sds *argv;
int argc; int argc;
@ -1147,11 +1198,24 @@ sds ACLLoadFromFile(const char *filename) {
ACLFreeUser(fakeuser); ACLFreeUser(fakeuser);
sdsfreesplitres(lines,totlines); sdsfreesplitres(lines,totlines);
/* Chec if we found errors and react accordingly. */
if (sdslen(errors) == 0) { if (sdslen(errors) == 0) {
/* 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. */
user *new = ACLGetUserByName("default",7);
ACLCopyUser(DefaultUser,new);
ACLFreeUser(new);
raxInsert(Users,(unsigned char*)"default",7,DefaultUser,NULL);
sdsfree(errors); sdsfree(errors);
return NULL; return NULL;
} else { } else {
return sdstrim(errors," "); ACLFreeUsersSet(Users);
Users = old_users;
errors = sdscat(errors,"WARNING: ACL errors detected, no change to the previously active ACL rules was performed");
return errors;
} }
} }