Skip to content

Commit 5bc9370

Browse files
authored
Merge branch 'releases/25.8.13' into backports/25.8.13/90761
2 parents 21a913f + 816a941 commit 5bc9370

39 files changed

+836
-18
lines changed

ci/jobs/scripts/check_style/aspell-ignore/en/aspell-dict.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,7 @@ atanh
14221422
atomicity
14231423
auth
14241424
authType
1425+
authenticatedUser
14251426
authenticator
14261427
authenticators
14271428
autocompletion

ci/jobs/scripts/check_style/various_checks.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ for test_case in "${tests_with_replicated_merge_tree[@]}"; do
7575
done
7676

7777
# Check for existence of __init__.py files
78-
for i in "${ROOT_PATH}"/tests/integration/test_*; do FILE="${i}/__init__.py"; [ ! -f "${FILE}" ] && echo "${FILE} should exist for every integration test"; done
78+
# for i in "${ROOT_PATH}"/tests/integration/test_*; do FILE="${i}/__init__.py"; [ ! -f "${FILE}" ] && echo "${FILE} should exist for every integration test"; done
7979

8080
# Check for executable bit on non-executable files
8181
find $ROOT_PATH/{src,base,programs,utils,tests,docs,cmake} '(' -name '*.cpp' -or -name '*.h' -or -name '*.sql' -or -name '*.j2' -or -name '*.xml' -or -name '*.reference' -or -name '*.txt' -or -name '*.md' ')' -and -executable | grep -P '.' && echo "These files should not be executable."
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
description: 'Documentation for EXECUTE AS Statement'
3+
sidebar_label: 'EXECUTE AS'
4+
sidebar_position: 53
5+
slug: /sql-reference/statements/execute_as
6+
title: 'EXECUTE AS Statement'
7+
doc_type: 'reference'
8+
---
9+
10+
# EXECUTE AS Statement
11+
12+
Allows to execute queries on behalf of a different user.
13+
14+
## Syntax {#syntax}
15+
16+
```sql
17+
EXECUTE AS target_user;
18+
EXECUTE AS target_user subquery;
19+
```
20+
21+
The first form (without `subquery`) sets that all the following queries in the current session will be executed on behalf of the specified `target_user`.
22+
23+
The second form (with `subquery`) executes only the specified `subquery` on behalf of the specified `target_user`.
24+
25+
In order to work both forms require server setting [allow_impersonate_user](/operations/server-configuration-parameters/settings#allow_impersonate_user)
26+
to be set to `1` and the `IMPERSONATE` privilege to be granted. For example, the following commands
27+
```sql
28+
GRANT IMPERSONATE ON user1 TO user2;
29+
GRANT IMPERSONATE ON * TO user3;
30+
```
31+
allow user `user2` to execute commands `EXECUTE AS user1 ...` and also allow user `user3` to execute commands as any user.
32+
33+
While impersonating another user function [currentUser()](/sql-reference/functions/other-functions#currentUser) returns the name of that other user,
34+
and function [authenticatedUser()](/sql-reference/functions/other-functions#authenticatedUser) returns the name of the user who has been actually authenticated.
35+
36+
## Examples {#examples}
37+
38+
```sql
39+
SELECT currentUser(), authenticatedUser(); -- outputs "default default"
40+
CREATE USER james;
41+
EXECUTE AS james SELECT currentUser(), authenticatedUser(); -- outputs "james default"
42+
```

src/Access/Common/AccessType.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ enum class AccessType : uint8_t
290290
M(SHOW_QUOTAS, "SHOW CREATE QUOTA", GLOBAL, SHOW_ACCESS) \
291291
M(SHOW_SETTINGS_PROFILES, "SHOW PROFILES, SHOW CREATE SETTINGS PROFILE, SHOW CREATE PROFILE", GLOBAL, SHOW_ACCESS) \
292292
M(SHOW_ACCESS, "", GROUP, ACCESS_MANAGEMENT) \
293+
M(IMPERSONATE, "EXECUTE AS", USER_NAME, ACCESS_MANAGEMENT) \
293294
M(ACCESS_MANAGEMENT, "", GROUP, ALL) \
294295
M(SHOW_NAMED_COLLECTIONS, "SHOW NAMED COLLECTIONS", NAMED_COLLECTION, NAMED_COLLECTION_ADMIN) \
295296
M(SHOW_NAMED_COLLECTIONS_SECRETS, "SHOW NAMED COLLECTIONS SECRETS", NAMED_COLLECTION, NAMED_COLLECTION_ADMIN) \

src/Access/UsersConfigAccessStorage.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,20 @@ namespace
114114

115115
UserPtr parseUser(
116116
const Poco::Util::AbstractConfiguration & config,
117-
const String & user_name,
117+
String user_name,
118118
const std::unordered_set<UUID> & allowed_profile_ids,
119119
const std::unordered_set<UUID> & allowed_role_ids,
120120
bool allow_no_password,
121121
bool allow_plaintext_password)
122122
{
123123
const bool validate = true;
124124
auto user = std::make_shared<User>();
125-
user->setName(user_name);
126125
String user_config = "users." + user_name;
126+
127+
/// If the user name contains a dot, it is escaped with a backslash when parsed from the config file.
128+
/// We need to remove the backslash to get the correct user name.
129+
Poco::replaceInPlace(user_name, "\\.", ".");
130+
user->setName(user_name);
127131
bool has_no_password = config.has(user_config + ".no_password");
128132
bool has_password_plaintext = config.has(user_config + ".password");
129133
bool has_password_sha256_hex = config.has(user_config + ".password_sha256_hex");

src/Common/Scheduler/SchedulerRoot.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,11 @@ class SchedulerRoot final : public ISchedulerNode
250250
}
251251

252252
Resource * current = nullptr; // round-robin pointer
253-
std::unordered_map<ISchedulerNode *, Resource> children; // resources by pointer
254253
std::atomic<bool> stop_flag = false;
255254
EventQueue events;
255+
/// Resources by pointer. Must be destroyed before the "events",
256+
/// because the descructor of ISchedulerNode might access the mutex in that queue.
257+
std::unordered_map<ISchedulerNode *, Resource> children;
256258
ThreadFromGlobalPool scheduler;
257259
};
258260

src/Core/ServerSettings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ The policy on how to perform a scheduling of CPU slots specified by `concurrent_
11391139
DECLARE(UInt64, threadpool_local_fs_reader_queue_size, 1000000, R"(The maximum number of jobs that can be scheduled on the thread pool for reading from local filesystem.)", 0) \
11401140
DECLARE(NonZeroUInt64, threadpool_remote_fs_reader_pool_size, 250, R"(Number of threads in the Thread pool used for reading from remote filesystem when `remote_filesystem_read_method = 'threadpool'`.)", 0) \
11411141
DECLARE(UInt64, threadpool_remote_fs_reader_queue_size, 1000000, R"(The maximum number of jobs that can be scheduled on the thread pool for reading from remote filesystem.)", 0) \
1142-
1142+
DECLARE(Bool, allow_impersonate_user, false, R"(Enable/disable the IMPERSONATE feature (EXECUTE AS target_user).)", 0) \
11431143

11441144
// clang-format on
11451145

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include <Functions/IFunction.h>
2+
#include <Functions/FunctionFactory.h>
3+
#include <Interpreters/Context.h>
4+
#include <DataTypes/DataTypeString.h>
5+
#include <Core/Field.h>
6+
7+
8+
namespace DB
9+
{
10+
namespace
11+
{
12+
13+
class FunctionAuthenticatedUser : public IFunction
14+
{
15+
const String user_name;
16+
17+
public:
18+
static constexpr auto name = "authenticatedUser";
19+
static FunctionPtr create(ContextPtr context)
20+
{
21+
return std::make_shared<FunctionAuthenticatedUser>(context->getClientInfo().authenticated_user);
22+
}
23+
24+
explicit FunctionAuthenticatedUser(const String & user_name_) : user_name{user_name_}
25+
{
26+
}
27+
28+
String getName() const override
29+
{
30+
return name;
31+
}
32+
size_t getNumberOfArguments() const override
33+
{
34+
return 0;
35+
}
36+
37+
DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override
38+
{
39+
return std::make_shared<DataTypeString>();
40+
}
41+
42+
bool isDeterministic() const override { return false; }
43+
44+
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
45+
46+
ColumnPtr executeImpl(const ColumnsWithTypeAndName &, const DataTypePtr &, size_t input_rows_count) const override
47+
{
48+
return DataTypeString().createColumnConst(input_rows_count, user_name);
49+
}
50+
};
51+
52+
}
53+
54+
REGISTER_FUNCTION(AuthenticatedUser)
55+
{
56+
factory.registerFunction<FunctionAuthenticatedUser>(FunctionDocumentation{
57+
.description=R"(
58+
If the session user has been switched using the EXECUTE AS command, this function returns the name of the original user that was used for authentication and creating the session.
59+
Alias: authUser()
60+
)",
61+
.syntax=R"(authenticatedUser())",
62+
.arguments={},
63+
.returned_value={R"(The name of the authenticated user.)", {"String"}},
64+
.examples{
65+
{"Usage example",
66+
R"(
67+
EXECUTE as u1;
68+
SELECT currentUser(), authenticatedUser();
69+
)",
70+
R"(
71+
┌─currentUser()─┬─authenticatedUser()─┐
72+
│ u1 │ default │
73+
└───────────────┴─────────────────────┘
74+
)"
75+
}},
76+
.introduced_in = {25, 11},
77+
.category = FunctionDocumentation::Category::Other
78+
});
79+
80+
factory.registerAlias("authUser", "authenticatedUser");
81+
}
82+
83+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#include <Interpreters/Access/InterpreterExecuteAsQuery.h>
2+
3+
#include <Access/AccessControl.h>
4+
#include <Access/User.h>
5+
#include <Core/Settings.h>
6+
#include <Core/ServerSettings.h>
7+
#include <Parsers/Access/ASTExecuteAsQuery.h>
8+
#include <Parsers/Access/ASTUserNameWithHost.h>
9+
#include <Interpreters/Context.h>
10+
#include <Interpreters/InterpreterFactory.h>
11+
#include <Interpreters/QueryFlags.h>
12+
#include <Interpreters/executeQuery.h>
13+
14+
15+
namespace DB
16+
{
17+
18+
namespace ErrorCodes
19+
{
20+
extern const int SUPPORT_IS_DISABLED;
21+
}
22+
23+
namespace ServerSetting
24+
{
25+
extern const ServerSettingsBool allow_impersonate_user;
26+
}
27+
28+
namespace
29+
{
30+
/// Creates another query context to execute a query as another user.
31+
ContextMutablePtr impersonateQueryContext(ContextPtr context, const String & target_user_name)
32+
{
33+
auto new_context = Context::createCopy(context->getGlobalContext());
34+
new_context->setClientInfo(context->getClientInfo());
35+
new_context->makeQueryContext();
36+
37+
const auto & database = context->getCurrentDatabase();
38+
if (!database.empty() && database != new_context->getCurrentDatabase())
39+
new_context->setCurrentDatabase(database);
40+
41+
new_context->setInsertionTable(context->getInsertionTable(), context->getInsertionTableColumnNames());
42+
new_context->setProgressCallback(context->getProgressCallback());
43+
new_context->setProcessListElement(context->getProcessListElement());
44+
45+
if (context->getCurrentTransaction())
46+
new_context->setCurrentTransaction(context->getCurrentTransaction());
47+
48+
if (context->getZooKeeperMetadataTransaction())
49+
new_context->initZooKeeperMetadataTransaction(context->getZooKeeperMetadataTransaction());
50+
51+
new_context->setUser(context->getAccessControl().getID<User>(target_user_name));
52+
53+
/// We need to update the client info to make currentUser() return `target_user_name`.
54+
new_context->setCurrentUserName(target_user_name);
55+
new_context->setInitialUserName(target_user_name);
56+
57+
auto changed_settings = context->getSettingsRef().changes();
58+
new_context->clampToSettingsConstraints(changed_settings, SettingSource::QUERY);
59+
new_context->applySettingsChanges(changed_settings);
60+
61+
return new_context;
62+
}
63+
64+
/// Changes the session context to execute all following queries in this session as another user.
65+
void impersonateSessionContext(ContextMutablePtr context, const String & target_user_name)
66+
{
67+
auto database = context->getCurrentDatabase();
68+
auto changed_settings = context->getSettingsRef().changes();
69+
70+
context->setUser(context->getAccessControl().getID<User>(target_user_name));
71+
72+
/// We need to update the client info to make currentUser() return `target_user_name`.
73+
context->setCurrentUserName(target_user_name);
74+
context->setInitialUserName(target_user_name);
75+
76+
context->clampToSettingsConstraints(changed_settings, SettingSource::QUERY);
77+
context->applySettingsChanges(changed_settings);
78+
79+
if (!database.empty() && database != context->getCurrentDatabase())
80+
context->setCurrentDatabase(database);
81+
}
82+
}
83+
84+
85+
BlockIO InterpreterExecuteAsQuery::execute()
86+
{
87+
if (!getContext()->getGlobalContext()->getServerSettings()[ServerSetting::allow_impersonate_user])
88+
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "IMPERSONATE feature is disabled, set allow_impersonate_user to 1 to enable");
89+
90+
const auto & query = query_ptr->as<const ASTExecuteAsQuery &>();
91+
String target_user_name = query.target_user->toString();
92+
getContext()->checkAccess(AccessType::IMPERSONATE, target_user_name);
93+
94+
if (query.subquery)
95+
{
96+
/// EXECUTE AS <user> <subquery>
97+
auto subquery_context = impersonateQueryContext(getContext(), target_user_name);
98+
return executeQuery(query.subquery->formatWithSecretsOneLine(), subquery_context, QueryFlags{ .internal = true }).second;
99+
}
100+
else
101+
{
102+
/// EXECUTE AS <user>
103+
impersonateSessionContext(getContext()->getSessionContext(), target_user_name);
104+
return {};
105+
}
106+
}
107+
108+
109+
void registerInterpreterExecuteAsQuery(InterpreterFactory & factory)
110+
{
111+
auto create_fn = [] (const InterpreterFactory::Arguments & args)
112+
{
113+
return std::make_unique<InterpreterExecuteAsQuery>(args.query, args.context);
114+
};
115+
factory.registerInterpreter("InterpreterExecuteAsQuery", create_fn);
116+
}
117+
118+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <Interpreters/IInterpreter.h>
4+
#include <Parsers/IAST_fwd.h>
5+
6+
7+
namespace DB
8+
{
9+
10+
class InterpreterExecuteAsQuery : public IInterpreter, WithMutableContext
11+
{
12+
public:
13+
InterpreterExecuteAsQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_) : WithMutableContext(context_), query_ptr(query_ptr_) {}
14+
BlockIO execute() override;
15+
16+
private:
17+
ASTPtr query_ptr;
18+
};
19+
20+
}

0 commit comments

Comments
 (0)