-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathQueryExecutionContext.java
More file actions
141 lines (127 loc) · 4.5 KB
/
Copy pathQueryExecutionContext.java
File metadata and controls
141 lines (127 loc) · 4.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.dbaagent.service;
import com.dbaagent.model.QueryExecutionOrigin;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Execution origin + mutation privileges for a SQL run.
*
* <p>{@code actorMayMutate} is true for built-in ADMIN and DBA (wired from
* {@code AccessControlService.currentUserMayMutateSql()}). It gates Editor/MCP
* DDL/DML; chat stays {@link MutationMode#READ_ONLY_ONLY}. The same flag is
* consulted by data-access policy during query execution so mutators are not
* blocked by schema redaction meant for read-only roles.
*/
public record QueryExecutionContext(
QueryExecutionOrigin origin,
MutationMode mutationMode,
String actorUsername,
boolean actorMayMutate,
boolean mutationConfirmed
) {
public enum MutationMode {
READ_ONLY_ONLY,
MAY_MUTATE
}
public static QueryExecutionContext chat() {
return new QueryExecutionContext(
QueryExecutionOrigin.CHAT,
MutationMode.READ_ONLY_ONLY,
resolveActorUsername(),
false,
false
);
}
public static QueryExecutionContext editor(String actorUsername, boolean actorMayMutate, boolean mutationConfirmed) {
return new QueryExecutionContext(
QueryExecutionOrigin.EDITOR,
actorMayMutate ? MutationMode.MAY_MUTATE : MutationMode.READ_ONLY_ONLY,
actorUsername,
actorMayMutate,
mutationConfirmed
);
}
public static QueryExecutionContext internal() {
return new QueryExecutionContext(
QueryExecutionOrigin.INTERNAL,
MutationMode.MAY_MUTATE,
null,
true,
true
);
}
public static QueryExecutionContext mcp(String actorUsername) {
return mcp(actorUsername, false);
}
public static QueryExecutionContext mcp(String actorUsername, boolean actorMayMutate) {
return mcp(actorUsername, actorMayMutate, false);
}
/**
* MCP / coding-agent SQL. Developers stay read-only. Admins and DBAs may run
* non-destructive DDL/DML after the same confirmation gate as the Editor.
* DROP and TRUNCATE stay blocked in {@link QueryExecutionPolicyService}.
*/
public static QueryExecutionContext mcp(
String actorUsername,
boolean actorMayMutate,
boolean mutationConfirmed
) {
return new QueryExecutionContext(
QueryExecutionOrigin.MCP,
actorMayMutate ? MutationMode.MAY_MUTATE : MutationMode.READ_ONLY_ONLY,
actorUsername,
actorMayMutate,
mutationConfirmed
);
}
public static QueryExecutionContext forSqlSurface(
boolean mcpBearer,
String actorUsername,
boolean actorMayMutate,
boolean mutationConfirmed
) {
if (mcpBearer) {
return mcp(actorUsername, actorMayMutate, mutationConfirmed);
}
return editor(actorUsername, actorMayMutate, mutationConfirmed);
}
public static QueryExecutionContext scheduled() {
return new QueryExecutionContext(
QueryExecutionOrigin.SCHEDULED,
MutationMode.MAY_MUTATE,
null,
true,
true
);
}
public static QueryExecutionContext api(String actorUsername) {
return api(actorUsername, false);
}
/**
* API / dashboard SQL is always read-only. The {@code actorMayMutate} flag here
* only influences data-access policy bypass (admins), not mutation mode.
*/
public static QueryExecutionContext api(String actorUsername, boolean actorMayMutate) {
return new QueryExecutionContext(
QueryExecutionOrigin.API,
MutationMode.READ_ONLY_ONLY,
actorUsername,
actorMayMutate,
false
);
}
private static String resolveActorUsername() {
String fromHolder = QueryActorContextHolder.currentUsername();
if (fromHolder != null && !fromHolder.isBlank()) {
return fromHolder;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
String name = authentication.getName();
if (name == null || name.isBlank() || "anonymousUser".equals(name)) {
return null;
}
return name;
}
}