connection updates

This commit is contained in:
Mark Gregory
2026-10-19 11:41:44 +01:00
parent a34faa3b29
commit 07c7e7d328
+66 -12
View File
@@ -5179,6 +5179,16 @@ private struct WorkbenchAccountManagerView: View {
@State private var password = "" @State private var password = ""
@State private var databaseName = "" @State private var databaseName = ""
@State private var privilege = "SELECT" @State private var privilege = "SELECT"
@State private var selectedPrivileges: Set<String> = ["SELECT"]
@State private var privilegeScope = "Schema"
@State private var selectedRole = "Custom"
private let rolePresets: [(String, String, [String])] = [
("Custom", "Choose individual privileges", []),
("Read Only", "Read tables and views", ["SELECT", "SHOW VIEW"]),
("Read / Write", "Read and modify records", ["SELECT", "INSERT", "UPDATE", "DELETE", "SHOW VIEW"]),
("DB Designer", "Create and alter schema objects", ["SELECT", "CREATE", "ALTER", "DROP", "INDEX", "REFERENCES", "CREATE VIEW", "SHOW VIEW", "TRIGGER"]),
("DB Manager", "Manage all supported schema privileges", ["SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP", "INDEX", "REFERENCES", "EXECUTE", "CREATE VIEW", "SHOW VIEW", "TRIGGER"])
]
@State private var previewSQL = "" @State private var previewSQL = ""
@State private var confirm = false @State private var confirm = false
@State private var running = false @State private var running = false
@@ -5217,11 +5227,47 @@ private struct WorkbenchAccountManagerView: View {
SecureField("Password", text: $password) SecureField("Password", text: $password)
} }
if action == .grant || action == .revoke { if action == .grant || action == .revoke {
TextField("Database", text: $databaseName) Picker("Privilege scope", selection: $privilegeScope) {
Picker("Privilege", selection: $privilege) { Text("Schema").tag("Schema")
ForEach(privileges, id: \.self) { item in Text("Global").tag("Global")
Text(item).tag(item)
} }
if privilegeScope == "Schema" {
TextField("Schema name", text: $databaseName)
.help("Enter an existing database name. Privileges apply to all objects in this schema.")
} else {
Text("Global privileges affect every database on this server. Review carefully before applying.")
.foregroundStyle(.orange)
}
Picker("Role preset", selection: $selectedRole) {
ForEach(rolePresets.map { $0.0 }, id: \.self) { role in
Text(role).tag(role)
}
}
.onChange(of: selectedRole) { _, newRole in
if let preset = rolePresets.first(where: { $0.0 == newRole }), newRole != "Custom" {
selectedPrivileges = Set(preset.2)
}
}
Text("Select the privileges to \(action == .grant ? "grant" : "revoke")")
.font(.headline)
LazyVGrid(columns: [GridItem(.adaptive(minimum: 165), spacing: 10)], alignment: .leading, spacing: 12) {
ForEach(privileges, id: \.self) { item in
Toggle(item, isOn: Binding(
get: { selectedPrivileges.contains(item) },
set: { enabled in
if enabled { selectedPrivileges.insert(item) }
else { selectedPrivileges.remove(item) }
selectedRole = "Custom"
}
))
.toggleStyle(.checkbox)
}
}
HStack {
Button("Select All") { selectedPrivileges = Set(privileges); selectedRole = "Custom" }
Button("Clear") { selectedPrivileges.removeAll(); selectedRole = "Custom" }
Spacer()
Text("\(selectedPrivileges.count) selected").foregroundStyle(.secondary)
} }
} }
} }
@@ -5241,7 +5287,7 @@ private struct WorkbenchAccountManagerView: View {
} }
} }
.padding(20) .padding(20)
.frame(width: 590) .frame(minWidth: 650, idealWidth: 760, minHeight: 520)
.onAppear { .onAppear {
if let selectedAccount { if let selectedAccount {
let parts = selectedAccount.split(separator: "\u{001f}", omittingEmptySubsequences: false) let parts = selectedAccount.split(separator: "\u{001f}", omittingEmptySubsequences: false)
@@ -5285,15 +5331,23 @@ private struct WorkbenchAccountManagerView: View {
case .unlock: case .unlock:
return ("ALTER USER \(account) ACCOUNT UNLOCK", "ALTER USER \(account) ACCOUNT UNLOCK") return ("ALTER USER \(account) ACCOUNT UNLOCK", "ALTER USER \(account) ACCOUNT UNLOCK")
case .grant, .revoke: case .grant, .revoke:
guard !databaseName.isEmpty, guard !selectedPrivileges.isEmpty, selectedPrivileges.allSatisfy({ privileges.contains($0) }) else {
databaseName.unicodeScalars.allSatisfy({ allowed.contains($0) }), throw AccountError.invalid("Select at least one privilege.")
privileges.contains(privilege) else {
throw AccountError.invalid("Enter a valid database name and privilege.")
} }
let db = "`\(databaseName)`.*" let target: String
if privilegeScope == "Global" {
target = "*.*"
} else {
guard !databaseName.isEmpty,
databaseName.unicodeScalars.allSatisfy({ allowed.contains($0) }) else {
throw AccountError.invalid("Enter a valid schema name.")
}
target = "`\(databaseName.replacingOccurrences(of: "`", with: "``"))`.*"
}
let rights = selectedPrivileges.sorted().joined(separator: ", ")
let command = action == .grant let command = action == .grant
? "GRANT \(privilege) ON \(db) TO \(account)" ? "GRANT \(rights) ON \(target) TO \(account)"
: "REVOKE \(privilege) ON \(db) FROM \(account)" : "REVOKE \(rights) ON \(target) FROM \(account)"
return (command, command) return (command, command)
} }
} }