typedefstruct { //用户名 sds name; /* The username as an SDS string. */ //用户的flag,用来做各种比对 uint32_t flags; /* See USER_FLAG_* */ //这个用户的密码,一个用户可以有多个密码,所以是用链表保存的 //这个链表的node是一个明文密码经过 `SHA256` 计算过后的 字符串 list *passwords; /* A list of SDS valid passwords for this user. */ //验证用户权限的选择器,该用户的权限保存在这个链表中 list *selectors; /* A list of selectors this user validates commands against. This list will always contain at least one selector for backwards compatibility. */ //缓存ACL命令的字符串 robj *acl_string; /* cached string represent of ACLs */ } user;
//user中 `selectors` 链表中的结构 typedefstruct { //这个 selectors 的flag 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 * allowed first-args, Redis will also check allowed_firstargs in order to * understand if the command can be executed. */
//使用 bit 记录允许的命令 uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64]; /* allowed_firstargs is used by ACL rules to block access to a command unless a * specific argv[1] is given. * * 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; //redis key 匹配规则的字符串链表 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. */
//redis channels 规则的字符串 链表 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. */ } aclSelector;
/* Structure used for handling key patterns with different key * based permissions. */ //`aclSelector` 中 patterns 链表中的结构 typedefstruct { int flags; /* The CMD_KEYS_* flags for this key pattern */ sds pattern; /* The pattern to match keys against */ } keyPattern;
voidACLLoadUsersAtStartup(void) { //先判断一下,如果配置了ACL文件的同时,又在redis.conf 中配置了用户,那么redis启动会失败 //ACL的配置只能在一个里面配置 if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) { serverLog(LL_WARNING, "Configuring Redis with users defined in redis.conf and at " "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 " "directly in your redis.conf, but not both."); exit(1); }
//处理从redis配置文件中加载的用户 if (ACLLoadConfiguredUsers() == C_ERR) { serverLog(LL_WARNING, "Critical error while loading ACLs. Exiting."); exit(1); }
//处理从acl文件中加载的用户 if (server.acl_filename[0] != '\0') { sds errors = ACLLoadFromFile(server.acl_filename); if (errors) { serverLog(LL_WARNING, "Aborting Redis startup because of ACL errors: %s", errors); sdsfree(errors); exit(1); } } }
if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) { serverLog(LL_WARNING,"Spaces not allowed in ACL usernames"); return C_ERR; }
user *u = ACLCreateUser(username,sdslen(username)); //做用户去重,一个用户只允许有一个 if (!u) { /* Only valid duplicate user is the default one. */ serverAssert(!strcmp(username, "default")); u = ACLGetUserByName("default",7); ACLSetUser(u,"reset",-1); }
/* Load every rule defined for this user. */ //遍历得到的规则,把这些规则赋给user,完成user信息的初始化 for (int j = 1; aclrules[j]; j++) { if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) { constchar *errmsg = ACLSetUserStringError(); serverLog(LL_WARNING,"Error loading ACL rule '%s' for " "the user named '%s': %s", aclrules[j],aclrules[0],errmsg); 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; }
//遍历文件中所有的行,开始加载user for (int i = 0; i < totlines; i++) ............ 省略读文件部分............... //创建一个User user *u = ACLCreateUser(argv[1],sdslen(argv[1]));
//如果用户已经存在了,跳过 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. */ int merged_argc; //这个函数的作用是把读到的字符串,提取成具体的user属性的数组 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); }
int syntax_error = 0; for (int j = 0; j < merged_argc; j++) { acl_args[j] = sdstrim(acl_args[j],"\t\r\n"); //遍历上面得到的手势,给创建的user设置属性 if (ACLSetUser(u,acl_args[j],sdslen(acl_args[j])) != C_OK) { constchar *errmsg = ACLSetUserStringError(); 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); } elseif (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; } } }
for (int i = 0; i < merged_argc; i++) sdsfree(acl_args[i]); zfree(acl_args);
/* 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) { sdsfreesplitres(argv,argc); continue; }
sdsfreesplitres(argv,argc); }
sdsfreesplitres(lines,totlines);
/* Check if we found errors and react accordingly. */ if (sdslen(errors) == 0) { //判断一下,是否加载了新的`default` user,如果有的话把旧的default user释放掉,用新加载的 user *new_default = ACLGetUserByName("default",7); if (!new_default) { new_default = ACLCreateDefaultUser(); }
ACLCopyUser(DefaultUser,new_default); ACLFreeUser(new_default); raxInsert(Users,(unsignedchar*)"default",7,DefaultUser,NULL); raxRemove(old_users,(unsignedchar*)"default",7,NULL); ACLFreeUsersSet(old_users); sdsfree(errors); returnNULL; } else { //如果出错了,数据回滚 ACLFreeUsersSet(Users); Users = old_users; errors = sdscat(errors,"WARNING: ACL errors detected, no change to the previously active ACL rules was performed"); return errors; } }
/* Check if the command exists. We can't check the * first-arg to see if it is valid. */ if (cmd == NULL) { zfree(copy); errno = ENOENT; return C_ERR; }
if (cmd->subcommands_dict) { //这个命令有子命令,处理子命令 cmd = ACLLookupCommand(op+1); if (cmd == NULL) { zfree(copy); errno = ENOENT; return C_ERR; } ACLChangeSelectorPerm(selector,cmd,1); } 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. */
/* Add the first-arg to the list of valid ones. */ 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); ACLAddAllowedFirstArg(selector,cmd->id,sub); }
/* Handle the two different forms here. The form with two arguments * will just use "default" as username. */ robj *username, *password; if (c->argc == 2) { //处理两种参数的情况,如果默认用户是没有密码的, 返回错谁 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; }
username = shared.default_username; password = c->argv[1]; } else { //处理三种参数的情况 username = c->argv[1]; password = c->argv[2]; redactClientCommandArgument(c, 2); } //校验用户名和密码是否匹配 if (ACLAuthenticateUser(c,username,password) == C_OK) { addReply(c,shared.ok); } else { addReplyError(c,"-WRONGPASS invalid username-password pair or user is disabled."); } }
但是如果直接用id的值左移来计算,uint64_t bit = 1ULL << id 假设有100个cmd,id的值就是100,那就需要一个整数是100位的长度。但是现在C语言的最大整数是64位长度。显然是不够用的。所以这里在计算的时候,会把命令先分组,让命令落在[0,63]这个范围内,然后再对组内的数字进行左移计算。这样,使用长度是64的 uint64_t 的数组就能完成 1024个命令的bit记录了。