diff --git a/docker/group/config.conf b/docker/group/config.conf index 65059b903..b82452108 100644 --- a/docker/group/config.conf +++ b/docker/group/config.conf @@ -27,7 +27,7 @@ database "group_db" { pool_routing "internal" pool "session" - group_query "select pg_has_role('%s', 'group1', 'MEMBER')" + group_query "SELECT rolname FROM pg_roles WHERE pg_has_role(rolname, 'group1', 'member');" } user "group_user2" { @@ -52,7 +52,7 @@ database "group_db" { pool_routing "internal" pool "session" - group_query "select pg_has_role('%s', 'group2', 'MEMBER')" + group_query "SELECT rolname FROM pg_roles WHERE pg_has_role(rolname, 'group2', 'member');" } user "group_user4" { diff --git a/docker/group/test_group.sh b/docker/group/test_group.sh index 7ea3abed5..4292b50d9 100755 --- a/docker/group/test_group.sh +++ b/docker/group/test_group.sh @@ -19,8 +19,17 @@ for user in "${users[@]}"; do } done -psql -h localhost -p 6432 -U postgres -c "GRANT group1 TO group_user1;" group_db +ody-stop + +psql -h localhost -p 5432 -U postgres -c "GRANT group1 TO group_user2;" group_db +psql -h localhost -p 5432 -U postgres -c "GRANT group1 TO group_user4;" group_db +psql -h localhost -p 5432 -U postgres -c "GRANT group2 TO group_user4;" group_db +psql -h localhost -p 5432 -U postgres -c "GRANT group1 TO group_user1;" group_db + +/usr/bin/odyssey /group/config.conf + sleep 1 + psql -h localhost -p 6432 -U group_user1 -c "SELECT 1" group_db >/dev/null 2>&1 || { echo "ERROR: group auth apply for over user at config" @@ -33,8 +42,6 @@ psql -h localhost -p 6432 -U group_user1 -c "SELECT 1" group_db >/dev/null 2>&1 exit 1 } -psql -h localhost -p 6432 -U postgres -c "GRANT group1 TO group_user2;" group_db -sleep 1 psql -h localhost -p 6432 -U group_user2 -c "SELECT 1" group_db >/dev/null 2>&1 && { echo "ERROR: group auth not apply" @@ -47,9 +54,6 @@ psql -h localhost -p 6432 -U group_user2 -c "SELECT 1" group_db >/dev/null 2>&1 exit 1 } -psql -h localhost -p 6432 -U postgres -c "GRANT group1 TO group_user4;" group_db -psql -h localhost -p 6432 -U postgres -c "GRANT group2 TO group_user4;" group_db -sleep 1 PGPASSWORD=password1 psql -h localhost -p 6432 -U group_user4 -c "SELECT 1" group_db >/dev/null 2>&1 && { echo "ERROR: group auth not accepted down group" @@ -61,6 +65,7 @@ PGPASSWORD=password1 psql -h localhost -p 6432 -U group_user4 -c "SELECT 1" grou exit 1 } + PGPASSWORD=password2 psql -h localhost -p 6432 -U group_user4 -c "SELECT 1" group_db >/dev/null 2>&1 || { echo "ERROR: group auth not apply" diff --git a/sources/config_reader.c b/sources/config_reader.c index 23bd0de9b..1e88df4f4 100644 --- a/sources/config_reader.c +++ b/sources/config_reader.c @@ -1893,6 +1893,8 @@ static int od_config_reader_group(od_config_reader_t *reader, char *db_name, if (!od_config_reader_string(reader, &group_name)) return NOT_OK_RESPONSE; + // TODO: need to find a way to create internal rules for a specific database + char route_usr[strlen("group_") + strlen(group_name) + 1]; char route_db[strlen("group_") + strlen(group_name) + 1]; snprintf(route_usr, sizeof route_usr, "%s%s", "group_", group_name); @@ -1945,7 +1947,7 @@ static int od_config_reader_group(od_config_reader_t *reader, char *db_name, // force several settings group->storage_db = rule->storage_db; group->storage_user = rule->storage_user; - rule->pool->routing = OD_RULE_POOL_INTERVAL; + rule->pool->routing = OD_RULE_POOL_INTERNAL; return OK_RESPONSE; @@ -2004,7 +2006,7 @@ static inline int od_config_reader_watchdog(od_config_reader_t *reader, // force several settings watchdog->storage_db = rule->storage_db; watchdog->storage_user = rule->storage_user; - rule->pool->routing = OD_RULE_POOL_INTERVAL; + rule->pool->routing = OD_RULE_POOL_INTERNAL; return OK_RESPONSE; } diff --git a/sources/group.c b/sources/group.c index cfa902194..ded068a8a 100644 --- a/sources/group.c +++ b/sources/group.c @@ -30,15 +30,7 @@ int od_group_free(od_group_t *group) return OK_RESPONSE; } -void od_group_qry_format(char *qry, char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - int len = od_vsnprintf(qry, OD_QRY_MAX_SZ, fmt, args); - va_end(args); -} - -int od_group_parse_val_datarow(machine_msg_t *msg, int *is_group_member) +int od_group_parse_val_datarow(machine_msg_t *msg, char **group_member) { char *pos = (char *)machine_msg_data(msg) + 1; uint32_t pos_size = machine_msg_size(msg) - 1; @@ -66,15 +58,21 @@ int od_group_parse_val_datarow(machine_msg_t *msg, int *is_group_member) goto error; } - if (strcmp(pos, "f") == 0) { - *is_group_member = 0; - } else if (strcmp(pos, "t") == 0) { - *is_group_member = 1; - } else { - goto error; - } + *group_member = strdup(pos); return OK_RESPONSE; error: return NOT_OK_RESPONSE; } + +od_group_member_name_item_t *od_group_member_name_item_add(od_list_t *members) +{ + od_group_member_name_item_t *item; + item = (od_group_member_name_item_t *)malloc(sizeof(*item)); + if (item == NULL) + return NULL; + memset(item, 0, sizeof(*item)); + od_list_init(&item->link); + od_list_append(members, &item->link); + return item; +} diff --git a/sources/group.h b/sources/group.h index 615bffc48..99b43ebf4 100644 --- a/sources/group.h +++ b/sources/group.h @@ -26,8 +26,16 @@ struct od_group { od_list_t link; }; +typedef struct od_group_member_name_item od_group_member_name_item_t; + +struct od_group_member_name_item { + char *value; + int is_checked; + od_list_t link; +}; + int od_group_free(od_group_t *); -void od_group_qry_format(char *, char *, ...); -int od_group_parse_val_datarow(machine_msg_t *, int *); +int od_group_parse_val_datarow(machine_msg_t *, char **); +od_group_member_name_item_t *od_group_member_name_item_add(od_list_t *); #endif /* ODYSSEY_GROUP_CHECK_ITER_INTERVAL */ \ No newline at end of file diff --git a/sources/pool.c b/sources/pool.c index a85ec1401..3fabcfc0d 100644 --- a/sources/pool.c +++ b/sources/pool.c @@ -98,7 +98,7 @@ int od_rule_matches_client(od_rule_pool_t *pool, od_pool_client_type_t t) { switch (t) { case OD_POOL_CLIENT_INTERNAL: - return pool->routing == OD_RULE_POOL_INTERVAL; + return pool->routing == OD_RULE_POOL_INTERNAL; case OD_POOL_CLIENT_EXTERNAL: return pool->routing == OD_RULE_POOL_CLIENT_VISIBLE; default: diff --git a/sources/pool.h b/sources/pool.h index 351d8b4fc..bd85f890c 100644 --- a/sources/pool.h +++ b/sources/pool.h @@ -16,7 +16,7 @@ typedef enum { } od_rule_pool_type_t; typedef enum { - OD_RULE_POOL_INTERVAL, + OD_RULE_POOL_INTERNAL, OD_RULE_POOL_CLIENT_VISIBLE, } od_rule_routing_type_t; diff --git a/sources/router.c b/sources/router.c index f1176bb2b..3b1a0b6e9 100644 --- a/sources/router.c +++ b/sources/router.c @@ -358,12 +358,10 @@ od_router_status_t od_router_route(od_router_t *router, od_client_t *client) int salen; struct sockaddr *saddr; int rc; - od_address_range_t default_address_range; switch (client->type) { case OD_POOL_CLIENT_INTERNAL: - default_address_range = od_address_range_create_default(); rule = od_rules_forward(&router->rules, startup->database.value, - startup->user.value, &default_address_range, 1); + startup->user.value, NULL, 1); break; case OD_POOL_CLIENT_EXTERNAL: salen = sizeof(sa); diff --git a/sources/rules.c b/sources/rules.c index 4447232fa..4d536796c 100644 --- a/sources/rules.c +++ b/sources/rules.c @@ -218,7 +218,8 @@ void od_rules_group_checker_run(void *arg) group->route_db, strlen(group->route_db) + 1); machine_msg_t *msg; - int is_group_member; + int is_group_member = 0; + char *group_member; int rc; /* route */ @@ -237,111 +238,149 @@ void od_rules_group_checker_run(void *arg) } for (;;) { - od_list_t *i = i_copy; - od_list_foreach_with_start(&rules->rules, i) - { - od_rule_t *rule; - rule = od_container_of(i, od_rule_t, link); - - if (rule->obsolete) - continue; - if (rule->pool->routing == OD_RULE_POOL_INTERVAL) - continue; - if (rule->pool->routing != OD_RULE_POOL_CLIENT_VISIBLE) - continue; - if (rule->db_is_default == 1 || - rule->user_is_default == 1) - continue; - - /* attach client to some route */ - status = od_router_attach(router, group_checker_client, - false); - od_debug( - &instance->logger, "group_checker", - group_checker_client, NULL, - "attaching group_checker client to backend connection status: %s", - od_router_status_to_str(status)); + /* attach client to some route */ + status = od_router_attach(router, group_checker_client, + false); + od_debug( + &instance->logger, "group_checker", + group_checker_client, NULL, + "attaching group_checker client to backend connection status: %s", + od_router_status_to_str(status)); - if (status != OD_ROUTER_OK) { + if (status != OD_ROUTER_OK) { + /* 1 second soft interval */ + machine_sleep(1000); + continue; + } + od_server_t *server; + server = group_checker_client->server; + od_debug(&instance->logger, "group_checker", + group_checker_client, server, + "attached to server %s%.*s", + server->id.id_prefix, + (int)sizeof(server->id.id), server->id.id); + + /* connect to server, if necessary */ + if (server->io.io == NULL) { + rc = od_backend_connect(server, "group_checker", + NULL, + group_checker_client); + if (rc == NOT_OK_RESPONSE) { + od_debug( + &instance->logger, + "group_checker", + group_checker_client, server, + "backend connect failed, retry after 1 sec"); + od_router_close(router, + group_checker_client); /* 1 second soft interval */ machine_sleep(1000); continue; } - od_server_t *server; - server = group_checker_client->server; - od_debug(&instance->logger, "group_checker", - group_checker_client, server, - "attached to server %s%.*s", - server->id.id_prefix, - (int)sizeof(server->id.id), server->id.id); - - /* connect to server, if necessary */ - if (server->io.io == NULL) { - rc = od_backend_connect(server, "group_checker", - NULL, - group_checker_client); - if (rc == NOT_OK_RESPONSE) { - od_debug( - &instance->logger, - "group_checker", - group_checker_client, server, - "backend connect failed, retry after 1 sec"); - od_router_close(router, - group_checker_client); - /* 1 second soft interval */ - machine_sleep(1000); - continue; - } + } + + for (int retry = 0; retry < group->check_retry; + ++retry) { + if (od_backend_query_send(server, "group_checker", group->group_query, NULL, + strlen(group->group_query) + 1) == NOT_OK_RESPONSE) { + return NULL; } + + int response_is_read = 0; + od_list_t members; + od_list_init(&members); + od_group_member_name_item_t *member; + + while(1) { + msg = od_read(&server->io, UINT32_MAX); + if (msg == NULL) { + if (!machine_timedout()) { + od_error(&instance->logger, "group_checker", + server->client, server, + "read error: %s", + od_io_error(&server->io)); + } + } - for (int retry = 0; retry < group->check_retry; - ++retry) { - char *qry = (char *)malloc(OD_QRY_MAX_SZ * - sizeof(char)); - od_group_qry_format(qry, group->group_query, - rule->user_name); - msg = od_query_do(server, "group_checker", qry, - NULL); - free(qry); - - if (msg != NULL) { + kiwi_be_type_t type; + type = *(char *)machine_msg_data(msg); + + od_debug(&instance->logger, "group_checker", server->client, server, + "%s", kiwi_be_type_to_string(type)); + + switch (type) { + case KIWI_BE_ERROR_RESPONSE: + od_backend_error(server, "group_checker", machine_msg_data(msg), + machine_msg_size(msg)); { + rc = NOT_OK_RESPONSE; + response_is_read = 1; + break; + } + case KIWI_BE_DATA_ROW: { rc = od_group_parse_val_datarow( - msg, &is_group_member); + msg, &group_member); + member = od_group_member_name_item_add(&members); + member->value = group_member; + break; + } + case KIWI_BE_READY_FOR_QUERY: + od_backend_ready(server, machine_msg_data(msg), + machine_msg_size(msg)); + machine_msg_free(msg); - od_router_close(router, - group_checker_client); - } else { - od_debug( - &instance->logger, - "group_checker", - group_checker_client, server, - "receive msg failed, closing backend connection"); - rc = NOT_OK_RESPONSE; - od_router_close(router, - group_checker_client); + response_is_read = 1; + break; + default: break; } - if (rc == OK_RESPONSE) { - od_debug(&instance->logger, - "group_checker", - group_checker_client, server, - "group check result is %d", - is_group_member); + if (response_is_read) + break; + } - if (is_group_member && - rule->is_group_member == 0) { + od_router_close(router, group_checker_client); + + bool have_default = false; + od_list_t *i; + od_list_foreach(&members, i) { + od_group_member_name_item_t *member_name; + member_name = od_container_of(i, od_group_member_name_item_t, link); + + od_list_t *j = i_copy; + od_list_foreach_with_start(&rules->rules, j) + { + od_rule_t *rule; + rule = od_container_of(j, od_rule_t, link); + + if (rule->obsolete || + rule->pool->routing == OD_RULE_POOL_INTERNAL || + rule->db_is_default != group_rule->db_is_default) + continue; + + if (rule->user_is_default) { + have_default = true; + } else if (strcmp(member_name->value, rule->user_name) == 0) { void *argv[] = { rule, - group_rule }; + group_rule }; od_router_foreach( router, od_rule_update_auth, argv); + member_name->is_checked = 1; } - - break; } } + + // TODO: handle members with is_checked = 0. these rules should be inherited from the default one, if there is one + + if (rc == OK_RESPONSE) { + od_debug(&instance->logger, + "group_checker", + group_checker_client, server, + "group check success"); + break; + } + // retry } @@ -360,7 +399,7 @@ void od_rules_group_checker_run(void *arg) } /* 1 second soft interval */ - machine_sleep(1000); + machine_sleep(7000); } } @@ -552,7 +591,7 @@ od_rule_t *od_rules_forward(od_rules_t *rules, char *db_name, char *user_name, if (rule->obsolete) continue; if (pool_internal) { - if (rule->pool->routing != OD_RULE_POOL_INTERVAL) { + if (rule->pool->routing != OD_RULE_POOL_INTERNAL) { continue; } } else { @@ -631,7 +670,7 @@ od_rule_t *od_rules_match(od_rules_t *rules, char *db_name, char *user_name, rule = od_container_of(i, od_rule_t, link); /* filter out internal or client-vidible rules */ if (pool_internal) { - if (rule->pool->routing != OD_RULE_POOL_INTERVAL) { + if (rule->pool->routing != OD_RULE_POOL_INTERNAL) { continue; } } else { @@ -1075,13 +1114,13 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, } int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, - char *user_name, char *address_range_string) + char *user_name, od_address_range_t *address_range) { /* pooling mode */ if (!pool->type) { od_error(logger, "rules", NULL, NULL, "rule '%s.%s %s': pooling mode is not set", db_name, - user_name, address_range_string); + user_name, address_range->string_value); return NOT_OK_RESPONSE; } if (strcmp(pool->type, "session") == 0) { @@ -1093,7 +1132,7 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, } else { od_error(logger, "rules", NULL, NULL, "rule '%s.%s %s': unknown pooling mode", db_name, - user_name, address_range_string); + user_name, address_range->string_value); return NOT_OK_RESPONSE; } @@ -1102,15 +1141,23 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, od_debug( logger, "rules", NULL, NULL, "rule '%s.%s %s': pool routing mode is not set, assuming \"client_visible\" by default", - db_name, user_name, address_range_string); + db_name, user_name, address_range->string_value); } else if (strcmp(pool->routing_type, "internal") == 0) { - pool->routing = OD_RULE_POOL_INTERVAL; + pool->routing = OD_RULE_POOL_INTERNAL; } else if (strcmp(pool->routing_type, "client_visible") == 0) { - pool->routing = OD_RULE_POOL_CLIENT_VISIBLE; + pool->routing = OD_RULE_POOL_CLIENT_VISIBLE; } else { od_error(logger, "rules", NULL, NULL, "rule '%s.%s %s': unknown pool routing mode", db_name, - user_name, address_range_string); + user_name, address_range->string_value); + return NOT_OK_RESPONSE; + } + + if (pool->routing == OD_RULE_POOL_INTERNAL && !address_range->is_default) { + od_error( + logger, "rules", NULL, NULL, + "rule '%s.%s %s': internal rules must have default address_range", + db_name, user_name, address_range->string_value); return NOT_OK_RESPONSE; } @@ -1120,7 +1167,7 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, od_error( logger, "rules", NULL, NULL, "rule '%s.%s %s': prepared statements support in session pool makes no sence", - db_name, user_name, address_range_string); + db_name, user_name, address_range->string_value); return NOT_OK_RESPONSE; } @@ -1128,7 +1175,7 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, od_error( logger, "rules", NULL, NULL, "rule '%s.%s %s': pool discard is forbidden when using prepared statements support", - db_name, user_name, address_range_string); + db_name, user_name, address_range->string_value); return NOT_OK_RESPONSE; } @@ -1136,7 +1183,7 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, od_error( logger, "rules", NULL, NULL, "rule '%s.%s %s': pool smart discard is forbidden without using prepared statements support", - db_name, user_name, address_range_string); + db_name, user_name, address_range->string_value); return NOT_OK_RESPONSE; } @@ -1145,7 +1192,7 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, od_error( logger, "rules", NULL, NULL, "rule '%s.%s %s': cannot support prepared statements when 'DEALLOCATE ALL' present in discard string", - db_name, user_name, address_range_string); + db_name, user_name, address_range->string_value); return NOT_OK_RESPONSE; } } @@ -1228,7 +1275,7 @@ int od_rules_autogenerate_defaults(od_rules_t *rules, od_logger_t *logger) rule->pool->pool = OD_RULE_POOL_TRANSACTION; rule->pool->routing_type = strdup("internal"); - rule->pool->routing = OD_RULE_POOL_INTERVAL; + rule->pool->routing = OD_RULE_POOL_INTERNAL; rule->pool->size = OD_DEFAULT_INTERNAL_POLL_SZ; rule->enable_password_passthrough = true; @@ -1365,7 +1412,7 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, if (od_pool_validate(logger, rule->pool, rule->db_name, rule->user_name, - rule->address_range.string_value) == + &rule->address_range) == NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } diff --git a/sources/rules.h b/sources/rules.h index 12f218719..e6d605df5 100644 --- a/sources/rules.h +++ b/sources/rules.h @@ -128,8 +128,9 @@ struct od_rule { int catchup_checks; /* group */ - od_group_t *group; + od_group_t *group; // set if rule is group int is_group_member; + od_rule_t *group_rule; // set if is_group_member = 1 /* PostgreSQL options */ kiwi_vars_t vars; @@ -178,7 +179,6 @@ void od_rules_print(od_rules_t *, od_logger_t *); int od_rules_cleanup(od_rules_t *rules); /* rule */ -od_rule_t *od_rule_allocate(void); od_rule_t *od_rules_add(od_rules_t *); void od_rules_ref(od_rule_t *); void od_rules_unref(od_rule_t *);